ê�a)<û™¨p¶QÓ÷b@’Û�L²M¥‰VYx�{òÆ¤_€9‡�b1vn‚]öG$R¹öùšh�\Õãtâcl<…,L˜QfOâ(b¿�Úxz!öQvmâYçÀ¯=êUÉ&^xK~JDÊ0Kk™Š›=Tž'LHjïÑßÑ›~°J*|Ä[a}òÉ„^pWéw×Ä<•>·7ª Û/ÅúÍ�’~Â¼)Ém‹¬PqeÙ{�m�'W{—=óÖãTÔpŸTUgSòò—‘Ñ[€÷þ‡¯[‰GcLÓìÀ£‰Ù¯M	L¥m$aþ€í×7 �|ÖK˜Çç¸G™Käq-iÁÆ$Ùb˜‚5šƒ™&­²ª_­Oê~Š¥,ôÀÄe„-QZË:ŽÍ�·…¾³¤(¯æßÓOŽa[@ ­Pi¹S‡…ÌÙ»kßmlžÐ+·HdI^‰�o¬«Kìð„t^üØ–ìÚ¨’µiÄ±k­7ïŸi¾š~ÂHgŠâ+‚á¯-©ç„êàYa -b}Mò-Åô—…ÃúþðK¨ŠNqàf£Ca°$cšßéè™¥_›Vÿ¿~ÐÉ¬?Ê¶jno¶a¢’’)‡‘Ø_�©b_õûFø³ÒÒÀ¡faË.ƒÉr‚r °Ôùôyà dê3ÚDFç\Ù!ª˜›)JsßKÑxÄÆÞ•çfšÏùežA�P)'¤†÷FŽˆ=[b[ûhDÖ À”/˜ÇÈí– Êã	ˆA^±M9…Ðšé8ÀŽûüÔÞ'øc²—ƒºCñ½‘g„¤”ïà¾ H}¦ùD¸¥°Í³J}”å@„€ÆcŽ®À~<6|Qý Öªæ`,Ù¯˜”/˜ÇÈí– Êã	ˆAÎ
E¤e-ßû•¹-ŒéOX©AÏ°è�?]T¨°Ö‡[fàˆÆ/NË³bc<g*ñ%ãPB¾}}$ì$ÕŽŽiÆ”úZU«IÜ�z€EŒNÓ)‚Ô^-×I§ÊÜ”/˜ÇÈí– Êã	ˆA÷'Òd™´_Î,��ðºâ=ä]²W™xÀŽQÇïØÚ!XÁ®µ¥™”'½J†J¶­&à;øÌ-µ¯¯j”üV‘$ì¼ÚxOÏPIRt£Ê¾x@quOcF>1ßÿã�àoW¶¾€ö¸¢²8ˆ÷6ó’Ae&?�)tâ!ñ¢ Ji[­Ö{¡‘½]Úë¢8\lŠ›>�g}Ì.žó»FGbàRÇvç™Çý¸äH’…ƒË.‹»Þz”Säb€¸·±xG+ô}r#ÀU”æ4Dpp’ä¾ŽSD"÷Ø9×û@q�€@€½ôîD9yÓxÐÊË#s�—þZ%
±7g’ËmÚZTùXêÓúü¾¹'éå¶ì ‹„vš‘G©¡úkbqQZRQb>R—É]�[3è›–y÷¡9«z1}š2ÑÚßp6�þeIÅ” €¿®Ï7Î©³ëRcóÙ}œvŽ„ÐYšçÞ•¸qÄ¡¶nòí}±+sÕ GwÆü>?Õƒë bÐœàIæ#ë<Ò7¤åïæ7Šºo?Ï£”nt=hkÎ–¦¹®Ð"2<8²õCÜ}áJš­ºÕ(Ë¶³Cß úìÿt Y¬AnØí»R<õ(gê}ÞNºó’�`~9U\?°ˆä™BüÉpÇ˜XfÛ(Ø�FGJýŸm½++ÒiRBn·ÄÅ¹«ÕŽpÎü×‡õF½°™i‹�(Šš~ÿôøŒ_¿+;œ"(9�‡9°\üãC¨gºr@ ÍÒBÐŒÆ©B”/˜ÇÈí– Êã	ˆA¿¬¦	Ö”C[ ¯Ð¿óF¢çé$LKj¸Q·ÈºÞÒ1Êõ@›(ÌüÓX7*›�·ÌÌÔ%Ž]–Î0ªÀéMƒ””/˜ÇÈí– Êã	ˆAÀåÇGcÝ!úqU	f¸[*‘YOq5 7z‰Ë^Ò <.’’ê$Gü¦Þ‰uaº³Æßê"±´ ÿt¯RŸ¼2 ûöi
më,yª@?ï[t1ýºÜÉ.ÃCÛå­¬_Ué¦Å‹Ÿ/e0�P]OÜÇS_vzGñü»¶Â³Ô‹BZo»_*[ÇbÏ‹Å*à <ÖÒ”/˜ÇÈí– Êã	ˆAñd_u¸ oêÔ•ÂŸBËQE€`™ëZDoh%JNƒ/Ì¹äRµÏwú�Î$c‡ÉO¼ùG¢£(ÅÅÝí7÷šCöô0 žË½Îixä4]ßÅ+s7= š†ƒ`¶NÌäö‹þ/È¤Â–=ÛQç*Gàê|ûÃ†7¶äuíô;g74 ¼pÒŽ&ó}›>�g}Ì.žó»FGbàRÇ-ûbàßÞ£Üê¶Þj°f�2x.ž41çôj#O�äp  hþ¯/bE"ƒË@ë”/˜ÇÈí– Êã	ˆA¤Q…Ù­Žkú]Ì°û=YÀÌõ4¼(�ž”WÎì#» Áß£E¡6 ÷ˆƒ×qkÚŸ-ˆ¨hö­P8¦
Ï‰ôó€ž}Á�8ºA”ø`S¶¬AnØí»R<õ(gê}ÞNºè¢dv�]IžOˆZºC££"6êíwCåóÕjyÍý×ééð¥G#¿¯Ä­C–�¸åKxÈ:~mÿUÉ©>I�/)BûÀü«í*Š7¡uŒ”/˜ÇÈí– Êã	ˆA°N•4pÿîgAÜª<xþHðg ÇÑG“ñF¤	BU!dÊOÌüÿòD×C�qÖfÅlgå_Î_:RëðK¤öädæ`ÕÆa¨N˜&ôÃ‡E!5Ôîý'l.ç4ŒvI	úÆÊÌ&¾%@©+ön˜ÊŽìÝ¨ožCbPfŠ8{}ÃU¤ÛRqÚ†_k†o|Ê(ú‚9tw0Ï2MÈb°´õ	ovK[ã½×”/˜ÇÈí– Êã	ˆAòDÝûDÊuøq…q3áf¤Yú\e {Õ :‰‹¹Q+Ë7ìü`ÁÀ>NŽú´#5¾ïùêEè¯ñÕœ ¹;”/˜ÇÈí– Êã	ˆA´¦Õ	÷ötJM¨>EÞÕÌŠVé#Cñ‰‘ªÇ“‰ |„;(M‘vh»ø‘)¯qun¤²ã(²[¼`"¶zà.­;J—o”6hIZ™”Å½”/˜ÇÈí– Êã	ˆA/Ôu”R‰“ä-r\:�VPÑ†²D+0ðùŸzŸãsy3Þ�Öã©x'Ç‘,7,1òB´7�ýØ14ÊLdEæ­”/˜ÇÈí– Êã	ˆA‰Å^ñït…â"T*_Ø‹)\}²=RÙ]ÓÖmãú)ÿÆ×…Ë·h)å�i~¶�ËÀÉÍY"÷"¾£¸ØcíœŸú2ÿ=K�)æ@t§
9 n/ß  á£ �¦hwîóc9ôdÛZªI[Eß_|)”/˜ÇÈí– Êã	ˆAdüé;Æ¢?Ós¤I Î–(WÍ vºEÙ‘æ+�$zu_@GÞ=v2©�”/˜ÇÈí– Êã	ˆAïõ}kc|·µ¦þ*3˜&ˆeˆ-¡�9ÏRìô4zìOŒ(évaF>{G.Ù š«^Æv×A)žØ£›n†’ig“ò1Ch™AqW¡Hì�aƒ� «R´Ò+×K®P FÂ”/˜ÇÈí– Êã	ˆAŒ0$¡òç%ÝHý€Ú„§!LläxXô!nMûæ {â¯	 ¹+7<Oåj§9c†c‰LÔJŸŠ]Të·ø©}[®D•÷ î‚5Q¥V½4¯PÙ%•¢J·Y?ò÷*W0±¹<•ß%o‹IÙ\¦~º�Š?À6ÓCcnÄÒ²	=û™ÞÊ—ò0�Ó7=æT*î�Wø0anzfëý9ý¥=¸þ¸â1¨Ï9g[ œžÃ/MÍG&RWRuÎyù76\ºýÄh‡”/˜ÇÈí– Êã	ˆAd6ï	ä.x¼JI¦¨·îS×‡¤H›Ã$Šëë\g®„¬AnØí»R<õ(gê}ÞNºÎb˜ÞBVÀ‹B¤–†ÎRÂB²øÄ	G¬EBWJë…íþÉH¤:òål=�¨ ©Î+ýÙ¢AÑÃ3íÒK9X”/˜ÇÈí– Êã	ˆAævQôG�#@Àƒv"Þšy`§¨xt¶�Ä3ŠªE¤£êû zíù¾…Wõ®à‘Ø…öö`­§a!n˜~¸‹5½¼ÿ•¥>‰Ž˜¸F {Höð·ú×üË(b‹VÙ÷H½Þµ,z DG¦J�îq�Þ9¼fõ|êum0ýÍ#A¢ô•€-lÿWš´¢�ß¹‡$ösÓ¹1¼”/˜ÇÈí– Êã	ˆAR•ï©„äó›Ké2þ�«ÏÆqkölœH'táŒß"h^‘Q\IäŠ·Ö`A·÷’$êõgö™ÊŠÔkk^æ,Ù>*Û¤6…Ê›¦éhÍ¯6UÕÆ$z¾B*Âc¶?vÂ•y”ÎÉ&xÌÛRÒÿG•UHÙõŠ4mª‚p@¿—¨¸Æ™l�K„jT´È‡ »æT*î�Wø0anzfëY˜\ÖRaeßVÆ�2«
!-Zå 1O‚Ôì’t’ÎØ}'ü7‹á"ívgq¾øú¢”/˜ÇÈí– Êã	ˆA.P¥D:ÿ38€]ÑÑ$QÆâ#µ°þ*\åv
hÈÜ¬AnØí»R<õ(gê}ÞNºüO¤¨ÆÄôp„Ë.b¹Ä_ßÏh˜¾·ŽªhV©¹µJï<ºÏµ.[½`Øï×¥iXkÊ
(ÔœÔë÷(J«°ûpcƒƒ•aHA)@9M3,øÓ}÷²èlDï_.{m«Àæ*7ªäp ~›¹Àˆ)é,‡g\F¤ú++—PºúPmŽ•²9/�êõ$ûê�~:Á™BlE$Û”/˜ÇÈí– Êã	ˆAå¶¾¨“Ñ£Ý4©‡M§uýÔÔkåñü(+z’;Ù…¬&ã
Õåmê³üNø‡‘Ø…öö`­§a!nró$9êÍ‰wZ9õÍ>TB„>&	�Uª9ŸVéåð[$û*×½"H¿7'âqgdt@?ï[t1ýºÜÉ.Ã¡ç$	±Ž)�KÂè]«‡(ÜA¼¢ËÖ–R?
Bª˜öƒÂETc4æC¶ìþH8Ìd–1 (ˆÔ
#T„‘†Ê=þ8 £q3¾tÙa¡tá<˜'ŸiúÆ»wÎª”/˜ÇÈí– Êã	ˆA™¢uËÐœÚÝ2#Cº„¡¿Q�EýW]æñ=´C2ªOÏvï|PYxKŸùÖ¥ºêõgö™ÊŠÔkk^æ,Ùc²rŽ}èŠÁõålúêr¾à.œÆû{”iY>â            S‘5      S<\[S)\+GR>                  S0\94S’ j5       r \@R©                  S“5      S<\[SB\[S)\+GR>                  S0\94S” j5       r¡\@RÝ                  S“5      S<\[SB\[S0\94S• j5       r¢\@R—                  S–5      S<\[SB\[S0\94S— j5       r£\@Rµ                  S˜5      S<\[SB\[S)\+GRH                  S0\94S™ j5       r¥Sš r¦S› r§Sœ r¨SøS� jr©Sž rªSùSŸ jr«S  r¬\@R—                  S¡5      S<\[SB\[S)\+GRZ                  S0\94S¢ j5       r®\@R—                  S£5      S<\[SB\[S0\94S¤ j5       r¯S¥ r°\@R—                  S¦5      S<\[S)\+GRb                  S0\94S§ j5       r²\@R©                  S¨5      S<\[SB\[S)\+GRb                  S0\94S© j5       r³\@Rµ                  Sª5      S<\[SB\[S)\+GRh                  S0\94S« j5       rµ\@Rµ                  S¬5      S<\[SB\[S0\94S­ j5       r¶\@RÝ                  S¨5      S<\[SB\[S0\94S® j5       r·\@Rµ                  S¯5      S<\[SB\[S)\+GRp                  S0\94S° j5       r¹\@R—                  S±5      S<\[S)\+GRt                  S0\94S² j5       r»\@R—                  S³5      S<\[SB\[S)\+GRx                  S0\94S´ j5       r½\@R—                  Sµ5      S<\[S)\+GR|                  S0\94S¶ j5       r¿\@R—                  S·5      S<\[SB\[S)\+GRx                  S0\94S¸ j5       rÀS¹ rÁ\@R—                  Sº5      S<\[S)\+GR„                  S0\94S» j5       rÃ\@R©                  S¼5      S<\[SB\[S)\+GR„                  S0\94S½ j5       rÄ\@RÝ                  S¼5      S<\[SB\[S0\94S¾ j5       rÅS¿SÀSÁSÂSÃSÄSÅ.rÆS)\+GRŽ                  4SÆ jrÈ\@R—                  SÇ5      S<\[S)\+GRŽ                  S0\94SÈ j5       rÉ\@R©                  SÉ5      S<\[SB\[S)\+GRŽ                  S0\94SÊ j5       rÊ\@RÝ                  SÉ5      S<\[SB\[S0\94SË j5       rË\@R—                  SÌ5      S<\[S)\+GR˜                  S0\94SÍ j5       rÍ\@R©                  SÎ5      S<\[SB\[S)\+GR˜                  S0\94SÏ j5       rÎS)\+GRž                  4SÐ jrÐ\@R—                  SÑ5      S<\[S)\+GRž                  S0\94SÒ j5       rÑ\@R©                  SÓ5      S<\[SB\[S)\+GRž                  S0\94SÔ j5       rÒS)\+GR¦                  4SÕ jrÔ\@R—                  SÖ5      S<\[S)\+GR¦                  S0\94S× j5       rÕ\@R©                  SØ5      S<\[SB\[S)\+GR¦                  S0\94SÙ j5       rÖ\@R—                  SÚ5      S<\[SB\[S0\94SÛ j5       r×\@R‘                  SÜ5      SúS<\[S0\9SÝ\[SÞ\[Sß\[Sà\[4Sá jj5       rØSâ rÙ\@R‘                  Sã5      S<\[Sä\[SB\[S0\94Så j5       rÚ\@R—                  Sã5      \" Sæ5      Sç4S<\[Sä\[SB\[S0\9Sè\Sé\[4Sê jj5       rÛ\@R‘                  Së5      S<\[SB\[S0\94Sì j5       rÜ\@R‘                  Sí5      S<\[SB\[S0\94Sî j5       rÝ\@R‘                  Sï5      S<\[S0\94Sð j5       rÞ\@GR¿                  Sñ\" \-Sò-  Só9SòSô9  \@R‘                  Sõ5      Sö 5       ràg)ûé    N)Úasynccontextmanager)Údate)ÚPath)Ú	Annotated)Úurlparse)ÚFastAPIÚDependsÚHTTPExceptionÚRequestÚResponseÚ
UploadFileÚFile)ÚFileResponseÚJSONResponseÚHTMLResponse)ÚStaticFiles)ÚTrustedHostMiddleware)ÚImageÚUnidentifiedImageErroré   )Úencrypt_secretÚsend_message)Úbuild_estimate_pdf)ÚauthÚmodels)ÚROOTÚDATA_DIRÚdbÚ
initializeÚuidÚnowÚinsertÚupdateÚrecordÚauditÚ	next_code>   ÚleadsÚinvoicesÚservicesÚ	estimatesÚtransactionsÚservice_callsÚinventory_itemsÚpurchase_orders>   Újobsr'   Útasksr(   r)   Ú	customersr*   Ú
propertiesÚ
warrantiesr+   r,   Úcalendar_eventsr-   r.   >   r/   r1   r2   r+   r,   r-   r.   c                 óŽ   • [        U 5      nUR                  SS 5        SU R                  5       ;   a  [        U S   5      OSUS'   U$ )NÚsmtp_password_encFÚsmtp_password_set)ÚdictÚpopÚkeysÚbool)ÚrowÚdatas     Úapp/main.pyÚsafe_businessr?      sI   € Ü	ˆc‹€DØ‡H�HÐ  $Ô'Ø@SÐWZ×W_ÑW_ÓWaÓ@aœd 3Ð':Ñ#;Ô<Ðgl€DÐ	ÑØ€Kó    c                ó*   #   • [        5         S 7v •  g 7f©N)r   )Úapps    r>   ÚlifespanrD   &   s   é € ä„LÝ	ùs   ‚zLB Exterior Lighting CRMz0.5.8ÚCRM_API_DOCSÚ1z	/api/docsz/api/openapi.json)ÚtitleÚversionrD   Údocs_urlÚ	redoc_urlÚopenapi_urlÚCRM_ALLOWED_HOSTSzlocalhost,127.0.0.1,testserverÚ,)Úallowed_hostsÚhttpc              ƒ   ó¦  #   • U R                   S;  aè  U R                  R                  S5      n[        R                  " SS5      R                  S5      nU R                  R                   SU R                  R                  SS5       3nU R                  R                  S5      S	:X  d  U(       a  X$U4;  a  [        S
S0SS9$ U R                  R                  SS5      n [        U5      S:”  a  [        S
S0SS9$  U" U 5      I S h  v•N nSUR                  S'   SUR                  S'   SUR                  S'   SUR                  S'   SUR                  S'   U R                  R                  R                  S5      (       dn  U R                  R                  R                  S 5      (       dD  U R                  R                  S!;   d*  U R                  R                  R                  S"5      (       a  S#UR                  S$'   S%UR                  S&'   [        R                  (       a  S'UR                  S('   U$ ! [         a    [        S
S0SS9s $ f = f GNH7f))N)ÚGETÚHEADÚOPTIONSÚoriginÚCRM_PUBLIC_ORIGINÚ Ú/ú://Úhostzsec-fetch-sitez
cross-siteÚdetailz(Cross-origin requests are not permitted.i“  ©Ústatus_codezcontent-lengthÚ0i    zRequest is too large.é�  zInvalid request length.é�  ÚnosniffzX-Content-Type-OptionsÚDENYzX-Frame-Optionszsame-originzReferrer-Policyz(camera=(), microphone=(), geolocation=()zPermissions-PolicyzØdefault-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'zContent-Security-Policyz/api/z/static/)rW   z/index.htmlú/quote/zno-store, max-age=0úCache-Controlzno-cacheÚPragmazmax-age=31536000zStrict-Transport-Security)ÚmethodÚheadersÚgetÚosÚgetenvÚrstripÚurlÚschemer   ÚintÚ
ValueErrorÚpathÚ
startswithr   ÚSECURE)ÚrequestÚ	call_nextrT   ÚallowedÚownÚsizeÚresponses          r>   Úsecurityrx   2   sR  é € à‡~�~Ð5Ó5Ø—‘×$Ñ$ XÓ.ˆÜ—)’)Ð/°Ó3×:Ñ:¸3Ó?ˆØ—‘×#Ñ#Ð$ C¨¯©×(;Ñ(;¸FÀBÓ(GÐ'HÐIˆØ�?‰?×ÑÐ/Ó0°LÓ@ÆVÐPVÐdkÐ^lÓPlÜ Ð*TÐ UÐcfÑgÐgØ�‰×"Ñ"Ð#3°CÓ8ˆð	WÜ�4‹yÐ+Ó+Ü# XÐ.EÐ$FÐTWÑXÐXð ,ñ ˜wÓ'×'€HØ1:€H×ÑÐ-Ñ.Ø*0€H×ÑÐ&Ñ'Ø*7€H×ÑÐ&Ñ'Ø-W€H×ÑÐ)Ñ*ð 3M€H×ÑÐ.Ñ/Ø‡{�{×Ñ×"Ñ" 7×+Ñ+¨w¯{©{×/?Ñ/?×/JÑ/JÈ:×/VÑ/VÐZa×ZeÑZe×ZjÑZjð  oCó  [Cð  GN÷  GRñ  GR÷  GWñ  GW÷  Gbñ  Gbð  cl÷  Gmñ  GmØ,Aˆ×Ñ˜Ñ)Ø%/ˆ×Ñ˜Ñ"Ü‡{‡{Ø8Jˆ×ÑÐ4Ñ5Ø€Oøô ó 	WÜ Ð*CÐ DÐRUÑVÒVð	Wúâ'ùs7   ‚CIÃH2 Ã9IÄIÄD+IÈ2IÉIÉ
IÉIc              ƒ   ó"   #   • [        SS0SS9$ 7f)NrZ   z>This change conflicts with an existing record or relationship.é™  r[   )r   )rr   Úexcs     r>   Úintegrity_errorr|   N   s   é € ä˜Ð"bÐcÐqtÑuÐuùs   ‚z/api/healthc                  óz   • [        5        n U R                  S5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NzSELECT 1ÚstatusÚok)r   Úexecute)Úconns    r>   Úhealthr‚   S   s6   € ä	Œ�Ø�‰�ZÔ ÷ 
à�Tˆ?Ð÷ 
Œà�Tˆ?Ðús   ‹)©
:z/api/auth/statusc                  óè   • [        5        n [        U R                  S5      R                  5       5      nU R                  S5      R                  5       S   nS S S 5        WWS.$ ! , (       d  f       N= f)Nú,SELECT 1 FROM users WHERE is_super=1 LIMIT 1zSELECT COUNT(*) FROM businessesr   )Ú
configuredÚbusiness_count)r   r;   r€   Úfetchone)r�   Úreadyr†   s      r>   Úsetup_statusr‰   Z   s^   € ä	Œ�Ü�T—\‘\Ð"PÓQ×ZÑZÓ\Ó]ˆØŸ™Ð&GÓH×QÑQÓSÐTUÑVˆ÷ 
ð °Ñ@Ð@÷ 
�ús   ‹AA#Á#
A1z/api/auth/setupÚpayloadc                 óì  • [        SS9 nUR                  S5      R                  5       (       a  [        SS5      eUR                  S5      R                  5       (       a  [        SS5      e[        R
                  " XR                  U R                  U R                  SS9nS	nU R                  R                  5       (       a…  [        [        5       U R                  R                  5       U R                  U R                  U R                  S
S[!        5       [!        5       S9	n[#        USU5        [%        XUS   SSUS   US   5        [%        XS	SSUS   5        S	S	S	5        SWS.$ ! , (       d  f       N= f)zPBrowser-based first-run wizard. It is permanently disabled once an admin exists.T©Úwriter„   rz   z)Initial setup has already been completed.zSELECT 1 FROM users LIMIT 1zBUser records already exist. Use the server recovery tools instead.)Úis_superNr   r   )	ÚidÚnameÚemailÚphoneÚaddressÚactiveÚis_demoÚ
created_atÚ
updated_atÚ
businessesr�   z#Created first business during setupr�   z"Completed first-run platform setupÚusers)r…   Úbusiness)r   r€   r‡   r
   r   Úcreate_userÚ
admin_nameÚadmin_emailÚpasswordÚcompany_nameÚstripr8   r    Úcompany_emailÚcompany_phoneÚcompany_addressr!   r"   r%   )rŠ   r�   Úadminrš   s       r>   Úfirst_run_setupr¥   b   sV  € ô 
�$Š˜4Ø�<‰<ÐFÓG×PÑP×RÑRÜ Ð%PÓQÐQØ�<‰<Ð5Ó6×?Ñ?×AÑAÜ Ð%iÓjÐjÜ× Ò  ×'9Ñ'9¸7×;NÑ;NÐPW×P`ÑP`ÐkoÑpˆØˆØ×Ñ×%Ñ%×'Ñ'Üœs›u¨7×+?Ñ+?×+EÑ+EÓ+GÈw×OdÑOdØ")×"7Ñ"7À×AXÑAXØ#$¨a¼C»EÌcËeñUˆHô �4˜ xÔ0Ü�$˜x¨™~Ð/TÐVbÐdlÐmqÑdrÐt|ð  ~Dñ  uEô  FÜˆd˜4Ð!EÀwÐPUÐVZÑP[Ô\÷ 
ð ¨HÑ5Ð5÷ 
�ús   ŠEE%Å%
E3z/api/auth/loginrr   rw   c                 óZ   • [         R                  " XU R                  U R                  5      $ rB   )r   Úloginr‘   rž   )rŠ   rr   rw   s      r>   r§   r§   v   s   € ä�:Š:�g¨¯©¸×8HÑ8HÓIÐIr@   z/api/meÚuserc           	      óz  • [        5        nU S   (       a3  UR                  S5       Vs/ s H  n[        [        U5      SS9PM     nnO/UR                  SU S   45       Vs/ s H  n[        U5      PM     nnS S S 5        [        [        R
                  " U 5      WU S   S9$ s  snf s  snf ! , (       d  f       N:= f)	NrŽ   z.SELECT * FROM businesses ORDER BY is_demo,nameÚowner©Úrolez~SELECT b.*,m.role FROM businesses b
                JOIN memberships m ON m.business_id=b.id WHERE m.user_id=? ORDER BY b.namer�   Úcsrf)r¨   r˜   r­   )r   r€   r8   r?   r   Ú	safe_user)r¨   r�   Úrr˜   s       r>   Úmer°   {   sÐ   € ä	Œ�Ø�
×ØHLÏÉð  VFô  IGó  Hò  IGÀ1œ$œ}¨QÓ/°gÔ>ñ  IGˆJð  HˆJà48·L±Lð B^Ø`dÐeiÑ`jÐ_lô5nó oò 5n¨qœ-¨Ö*ñ 5nˆJð o÷	 
ô ”T—^’^ DÓ)°jÀtÈFÁ|ÑTÐTùò	 Hùòo÷	 
�ús(   ‹B,©B"ÁB,Á!B'Á6B,Â"
B,Â,
B:z/api/auth/logoutc           
      ó*  • [        SS9 nUR                  S[        R                  " U R                  R                  [        R                  S5      5      45        S S S 5        UR                  [        R                  SS9  SS0$ ! , (       d  f       N0= f)NTrŒ   z'DELETE FROM sessions WHERE token_hash=?rV   rW   )ro   r   )r   r€   r   ÚdigestÚcookiesrg   ÚCOOKIEÚdelete_cookie)rr   rw   r¨   r�   s       r>   Úlogoutr¶   †   sm   € ä	�$Š˜4Ø�‰Ð>ÄÇÂÈWÏ_É_×M`ÑM`Ôae×alÑalÐmoÓMpÓAqÐ@sÔt÷ 
à×Ñœ4Ÿ;™;¨SÐÑ1Ø�ˆ;Ð÷ 
�ús   ŠABÂ
Bz/api/auth/passwordc           
      óÂ  • [         R                  " US   U R                  5      (       d  [        SS5      eU R                  U R                  :X  a  [        SS5      e[        SS9 nUR                  S[         R                  " U R                  5      US   45        UR                  S	US   45        [        X!S S
SUS   5        S S S 5        SSS.$ ! , (       d  f       N= f)NÚpassword_hashr_   zCurrent password is incorrect.zChoose a different password.TrŒ   z9UPDATE users SET password_hash=?,must_change=0 WHERE id=?r�   ú$DELETE FROM sessions WHERE user_id=?zChanged passwordr™   z7Password changed. Sign in again with your new password.)r   Úmessage)	r   ÚverifyÚcurrent_passwordr
   Únew_passwordr   r€   Úhash_passwordr%   )rŠ   r¨   r�   s      r>   rž   rž   Ž   sÝ   € ä�;Š;�t˜OÑ,¨W×-EÑ-E×FÑFÜ˜CÐ @ÓAÐAØ×Ñ 7×#7Ñ#7Ó7Ü˜CÐ >Ó?Ð?Ü	�$Š˜4Ø�‰ÐPÔSW×SeÒSeÐfm×fzÑfzÓS{ð  }Að  BFñ  }Gð  SHô  	IØ�‰Ð;¸dÀ4¹j¸]ÔKÜˆd˜Ð/°¸¸T¹
ÔC÷ 
ð Ð YÑZÐZ÷	 
�ús   Á%ACÃ
Cz/api/profilec                 ór  • [         R                  " U R                  SS9S S n[         R                  " U R                  SS9S S n[	        SS9 nUR                  SU R                  U R                  U R                  X#US   45        [        XAS S	S
US   5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)N)rM   Ú:)Ú
separatorsi N  é'  TrŒ   zZUPDATE users SET name=?,phone=?,job_title=?,dashboard_json=?,preferences_json=? WHERE id=?r�   zUpdated profiler™   r   )
ÚjsonÚdumpsÚ	dashboardÚpreferencesr   r€   r�   r’   Ú	job_titler%   )rŠ   r¨   rÅ   Úprefsr�   s        r>   Úprofile_updaterÉ   ›   s²   € ä�jŠj˜×*Ñ*°yÑAÀ&À5ÐI€IÜ
�*Š*�W×(Ñ(°YÑ
?ÀÀÐ
G€EÜ	�$Š˜4Ø�‰ÐqØ—l‘l 7§=¡=°×1BÑ1BÀ9ÐSWÐX\ÑS]Ð^ô	`äˆd˜Ð.¨w°t¸D±zÔB÷ 
ð �ˆ;Ð÷	 
Œð �ˆ;Ðús   ÁAB%Â%
B6z/api/admin/businessesc                 óØ   • [         R                  " U 5        [        5        nUR                  S5       Vs/ s H  n[	        U5      PM     snsS S S 5        $ s  snf ! , (       d  f       g = f)Nao  SELECT b.*,
            (SELECT COUNT(*) FROM memberships m WHERE m.business_id=b.id) AS members,
            (SELECT COUNT(*) FROM customers c WHERE c.business_id=b.id) AS customers,
            (SELECT COUNT(*) FROM jobs j WHERE j.business_id=b.id AND j.status NOT IN ('completed','cancelled')) AS active_jobs
            FROM businesses b ORDER BY b.is_demo,b.name)r   Úplatformr   r€   r?   ©r¨   r�   r¯   s      r>   r˜   r˜   ¦   sX   € ä‡M‚M�$ÔÜ	Œ�Ø*.¯,©,ð 8;ô +<ó =ò +< Q”˜aÖ ñ +<ñ =÷ 
‰ùò=÷ 
�úó   ¡AµAÁ
AÁAÁ
A)c                 óF  • [         R                  " U5        [        U R                  5      [        U R                  5      :w  a  [        SS5      eU R                  SS1S9n[        SS[        5       0UDSS[        5       [        5       S	.D6n[        S
S9 n[        USU5        [        XAUS   SSUS   US   5        U R                  (       a1  [         R                  " XAUS   U R                  U R                  S5      OS nS S S 5        [        UWS9$ ! , (       d  f       N= f)Nr_   z;Provide both the owner name and email, or leave both blank.Ú
owner_nameÚowner_email©Úexcluder�   r   r   )r”   r•   r–   r—   TrŒ   r˜   zCreated businessr�   rª   )rš   rª   © )r   rË   r;   rÏ   rÐ   r
   Ú
model_dumpr8   r    r!   r   r"   r%   Ú
add_member)rŠ   r¨   r=   rš   r�   Úmembers         r>   Úcreate_businessr×   ±   s	  € ä‡M‚M�$ÔÜˆG×ÑÓ¤4¨×(;Ñ(;Ó#<Ó<Ü˜CÐ ]Ó^Ð^Ø×Ñ |°MÐ&BÐÐC€DÜÑY”s“uÐY˜tÐY¨1°QÄ#Ã%ÔSVÓSXÓY€HÜ	�$Š˜4Üˆt�L Ô*Üˆd˜ ™Ð'9¸,ÀxÐPTÁ~ÐV^Ð_eÑVfÔgØmt÷  nA÷  nA”—’ ¨8°D©>¸'×:LÑ:LÈW×M`ÑM`ÐahÔið  GKˆ÷ 
ô ˜¨Ñ/Ð/÷	 
�ús   ÂA+DÄ
D z"/api/admin/businesses/{bid}/activeÚbidc           	      óˆ  • [         R                  " U5        [        SS9 nUR                  SU 45      R	                  5       (       d  [        SS5      eUR                  S[        UR                  5      [        5       U 45        [        X2XR                  (       a  SOSS	U 5        S S S 5        S
S0$ ! , (       d  f       S
S0$ = f)NTrŒ   z#SELECT 1 FROM businesses WHERE id=?é”  úBusiness not found.z6UPDATE businesses SET active=?,updated_at=? WHERE id=?zEnabled businesszSuspended businessr˜   r   )
r   rË   r   r€   r‡   r
   rm   r”   r!   r%   ©rØ   rŠ   r¨   r�   s       r>   Úbusiness_activerÝ   ¿   s¨   € ä‡M‚M�$ÔÜ	�$Š˜4Ø�|‰|ÐAÀ3À&ÓI×RÑR×TÑTÜ Ð$9Ó:Ð:Ø�‰ÐMÌsÐSZ×SaÑSaÓObÔcfÓchÐilÐNmÔnÜˆd˜·.·.Ñ.ÐFZÐ[gÐhkÔl÷	 
ð
 �ˆ;Ð÷ 
Œð
 �ˆ;Ðús    BB0Â0
Cz/api/admin/usersc                 óX  • [         R                  " U 5        [        5        n/ nUR                  S5       HR  n[	        U5      nUR                  SUS   45       Vs/ s H  n[	        U5      PM     snUS'   UR                  U5        MT     UsS S S 5        $ s  snf ! , (       d  f       g = f)Nz«SELECT id,name,email,is_super,active,must_change,created_at,
            (SELECT COUNT(*) FROM memberships m WHERE m.user_id=u.id) AS businesses FROM users u ORDER BY namez¼SELECT m.business_id,m.role,b.name AS business_name,b.active AS business_active
                FROM memberships m JOIN businesses b ON b.id=m.business_id WHERE m.user_id=? ORDER BY b.namer�   Úmemberships)r   rË   r   r€   r8   Úappend)r¨   r�   Úpeopler¯   ÚpersonÚxs         r>   Úadmin_usersrä   Ê   s¬   € ä‡M‚M�$ÔÜ	Œ�ØˆØ—‘ð rö sˆAä˜“7ˆFØ48·L±Lð BpØqwÐx|Ñq}Ðpô5Aó #Bò 5A¨q¤4¨¦7ñ 5Añ #BˆF�=Ñ!à�M‰M˜&Ö!ñsð ÷ 
‰ùò
#B÷ 
�ús   ¡:BÁBÁ0BÂBÂ
B)c                 óÂ  • [         R                  " U5        [        R                  " S5      n/ n[	        SS9 nUR                  SU R                  45      R                  5       (       a  [        SS5      e[         R                  " X@R                  U R                  USS9n[        5       nU R                   HÖ  nUR                  U;   a  [        SS	5      eUR                  UR                  5        UR                  S
UR                  45      R                  5       nU(       d  [        SS5      e[        US[!        UR                  US   UR"                  S95        UR%                  [!        UR                  US   UR"                  S95        MØ     ['        XAS SSUS   [)        U5       S35        S S S 5        [         R*                  " W5      X2S.$ ! , (       d  f       N'= f)Né   TrŒ   z0SELECT 1 FROM users WHERE email=? COLLATE NOCASErz   z]An account with this email already exists. Use Manage access to assign it to more businesses.)Úmust_changer_   ú%A business can only be assigned once.ú)SELECT id,name FROM businesses WHERE id=?ú0One of the selected businesses no longer exists.rß   r�   ©Úbusiness_idÚuser_idr¬   r�   )rì   Úbusiness_namer¬   zCreated user accountr™   z business assignment(s))r¨   rß   Útemporary_password)r   rË   ÚsecretsÚtoken_urlsafer   r€   r‘   r‡   r
   r›   r�   Úsetrß   rì   Úaddr"   r8   r¬   rà   r%   Úlenr®   )	rŠ   r¨   rž   rß   r�   rÖ   ÚseenÚ
assignmentrš   s	            r>   Úadmin_create_userr÷   Ø   s›  € ä‡M‚M�$ÔÜ×"Ò" 2Ó&€HØ€KÜ	�$Š˜4Ø�<‰<ÐJÈGÏMÉMÐK[Ó\×eÑe×gÑgÜ ð  %Dó  Eð  EÜ×Ò §\¡\°'·-±-ÀÐUYÑZˆÜ‹UˆØ!×-Ô-ˆJØ×%Ñ%¨Ó-Ü# CÐ(OÓPÐPØ�H‰H�Z×+Ñ+Ô,Ø—\‘\Ð"MÈz×OeÑOeÐNgÓh×qÑqÓsˆHÞ¤=°Ð5gÓ#hÐhÜ�4˜¤d°z×7MÑ7MÐV\Ð]aÑVbÐhr×hwÑhwÑ&xÔyØ×Ñœt°
×0FÑ0FÐU]Ð^dÑUeÐku×kzÑkzÑ{Ö|ñ .ô 	ˆd˜Ð3°G¸FÀ4¹LÌCÐP[ÓL\ÐK]Ð]tÐIuÔv÷ 
ô —>’> &Ó)¸ÑbÐb÷ 
�ús   ¸E7GÇ
Gz"/api/admin/users/{rid}/membershipsÚridc                 ó,  • [         R                  " U5        XS   :X  a  [        SS5      e[        SS9 nUR	                  SU 45      R                  5       nU(       d  [        SS5      eUS	   (       a  [        SS
5      e[        5       n/ nUR                   H£  nUR                  U;   a  [        SS5      eUR                  UR                  5        UR	                  SUR                  45      R                  5       nU(       d  [        SS5      eUR                  UR                  UR                  US   45        M¥     UR	                  SU(       a  SR                  S U 5       5      OS-  U(       a  U /UQ7OU 45        UR	                  SU 45        U H  u  pšn[        US[        X�U
S95        M     UR	                  SU 45        [        X2S SSU SR                  S U 5       5      =(       d    S5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)Nr�   r_   zZPlatform administrators already have access to every business and do not need assignments.TrŒ   zSELECT * FROM users WHERE id=?rÚ   úUser not found.rŽ   z8Platform administrators do not use business memberships.rè   ré   rê   r�   zZUPDATE jobs SET assigned_user_id=NULL WHERE assigned_user_id=? AND business_id NOT IN (%s)rM   c              3   ó&   #   • U  H  nS v •  M	     g7f©Ú?NrÓ   ©Ú.0Ú_s     r>   Ú	<genexpr>Ú$admin_assign_user.<locals>.<genexpr>þ   s(   é € ð  ~Qò  LPð  GHõ  Bò  LPùó   ‚z''z'DELETE FROM memberships WHERE user_id=?rß   rë   r¹   zUpdated business assignmentsr™   ú, c              3   ó4   #   • U  H  u  po3 S U 3v •  M     g7f)z: NrÓ   )rÿ   r   r¬   r�   s       r>   r  r    s2   é € ð  RDò  xCÑhsÐhiÐosÐTZÐZ\Ð]aÐ\bÕRcò  xCùs   ‚zNo business accessr   )r   rË   r
   r   r€   r‡   rò   rß   rì   ró   rà   r¬   Újoinr"   r8   r%   )rø   rŠ   r¨   r�   Útargetrõ   Úassignmentsrö   rš   rØ   r¬   r   s               r>   Úadmin_assign_userr	  î   s8  € ä‡M‚M�$ÔØ
�4‰jÓÜ˜CÐ |Ó}Ð}Ü	�$Š˜4Ø�|‰|Ð<¸c¸VÓD×MÑMÓOˆÞœ]¨3Ð/@ÓAÐAØ�*×¤]°3Ð7qÓ%rÐrÜ‹Uˆ �KØ!×-Ô-ˆJØ×%Ñ%¨Ó-´]À3ÐGnÓ5oÐ/oØ�H‰H�Z×+Ñ+Ô,Ø—\‘\Ð"MÈz×OeÑOeÐNgÓh×qÑqÓsˆHÞ¤=°Ð5gÓ#hÐhØ×Ñ 
× 6Ñ 6°z·±ÀxÐPVÑGWÐXÖYñ .ð 	�‰Ðqö  UYÐux×u}Ñu}ñ  ~Qñ  LPó  ~Qô  vQð  _cñ  dö  uyð  gjð  fqð  lpò  fqð  @Cð  Eô  	FØ�‰Ð>À¸vÔFÛ%‰JˆC�QÜ�4˜¤d°sÈDÑ&QÖRñ &à�‰Ð;¸S¸FÔCÜˆd˜Ð;¸GÀCÈÏ	É	ñ  RDñ  xCó  RDó  ID÷  I\ð  H\ô  	]÷! 
ð" �ˆ;Ð÷# 
Œð" �ˆ;Ðús   ´GHÈ
Hz/api/admin/users/{rid}/activec           	      óÆ  • [         R                  " U5        XS   :X  a  [        SS5      e[        SS9 nUR	                  SU 45      R                  5       (       d  [        SS5      eUR	                  S	[        UR                  5      U 45        UR	                  S
U 45        [        X2S UR                  (       a  SOSSU 5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)Nr�   r_   z$You cannot disable your own account.TrŒ   úSELECT 1 FROM users WHERE id=?rÚ   rú   z$UPDATE users SET active=? WHERE id=?r¹   zEnabled accountzDisabled accountr™   r   )	r   rË   r
   r   r€   r‡   rm   r”   r%   )rø   rŠ   r¨   r�   s       r>   Úuser_activer    sÊ   € ä‡M‚M�$ÔØ
�4‰jÓÜ˜CÐ FÓGÐGÜ	�$Š˜4Ø�|‰|Ð<¸c¸VÓD×MÑM×OÑOÜ Ð$5Ó6Ð6Ø�‰Ð;¼SÀÇÁÓ=PÐQTÐ<UÔVØ�‰Ð;¸S¸FÔCÜˆd˜°'·.·.Ñ.ÐFXÐY`ÐadÔe÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðús   ´BCÃ
C z/api/admin/users/{rid}/resetc           	      ó¦  • [         R                  " U5        [        R                  " S5      n[	        SS9 nUR                  SU 45      R                  5       (       d  [        SS5      eUR                  S[         R                  " U5      U 45        UR                  SU 45        [        X1S S	S
U 5        S S S 5        SU0$ ! , (       d  f       SU0$ = f)Nræ   TrŒ   r  rÚ   rú   z9UPDATE users SET password_hash=?,must_change=1 WHERE id=?r¹   zReset account passwordr™   rï   )
r   rË   rð   rñ   r   r€   r‡   r
   r¾   r%   )rø   r¨   rž   r�   s       r>   Ú
reset_userr    s½   € ä‡M‚M�$ÔÜ×"Ò" 2Ó&€HÜ	�$Š˜4Ø�|‰|Ð<¸c¸VÓD×MÑM×OÑOÜ Ð$5Ó6Ð6Ø�‰ÐPÔRV×RdÒRdÐemÓRnÐorÐQsÔtØ�‰Ð;¸S¸FÔCÜˆd˜Ð5°g¸cÔB÷ 
ð ! Ð*Ð*÷ 
Œð ! Ð*Ð*ús   ¶A=B?Â?
Cz/api/admin/activityc                 óØ   • [         R                  " U 5        [        5        nUR                  S5       Vs/ s H  n[	        U5      PM     snsS S S 5        $ s  snf ! , (       d  f       g = f)NzASELECT * FROM audit ORDER BY created_at DESC,rowid DESC LIMIT 200)r   rË   r   r€   r8   rÌ   s      r>   Úplatform_activityr  "  sI   € ä‡M‚M�$ÔÜ	Œ�Ø!%§¡Ð.qÔ!rÓsÒ!r˜A”�Q–Ñ!rÑs÷ 
‰ùÚs÷ 
�úrÍ   z/api/b/{bid}/settingsc           
      ó  • [        SS9 n[        R                  " X2U S5        UR                  5       nUR	                  SUS   US   US   US   [        5       U 45        [        X2U S	S
U 5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   rª   zNUPDATE businesses SET name=?,email=?,phone=?,address=?,updated_at=? WHERE id=?r�   r‘   r’   r“   zUpdated business profiler˜   r   )r   r   Ú	authorizerÔ   r€   r!   r%   ©rØ   rŠ   r¨   r�   r=   s        r>   Úsettingsr  )  s’   € ä	�$Š˜4Ü�Š�t  WÔ-Ø×ÑÓ!ˆØ�‰ÐeØ˜6‘l 4¨¡=°°g±¸tÀI¹ÌsËuÐUXÐYô	[äˆd˜Ð6°|ÀCÔH÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðús   ŠA$A:Á:
Bz/api/b/{bid}/teamc                 óÞ   • [        5        n[        R                  " X!U 5        UR                  SU 45       Vs/ s H  n[	        U5      PM     snsS S S 5        $ s  snf ! , (       d  f       g = f)NzŽSELECT u.id,u.name,u.email,u.active,m.role FROM users u
            JOIN memberships m ON m.user_id=u.id WHERE m.business_id=? ORDER BY u.name)r   r   r  r€   r8   )rØ   r¨   r�   r¯   s       r>   Úteamr  4  sf   € ä	Œ�Ü�Š�t Ô%Ø!%§¡ð /ZØ[^ÐZ`ô"bó cò "b˜A”�Q–ñ "bñ c÷ 
‰ùòc÷ 
�ús   ‹-A¸AÁAÁAÁ
A,c           	      óè   • [        SS9 n[        R                  " X2U S5        [        R                  " X2XR                  UR
                  UR                  5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   rª   )r   r   r  rÕ   r�   r‘   r¬   rÜ   s       r>   Úteam_addr  <  sC   € ä	�$Š˜4Ü�Š�t  WÔ-Ü�Š˜t¨¯\©\¸'¿-¹-ÈÏÉÓU÷ 
��ús   ŠAA#Á#
A1c                 óè   • U R                  SX45      R                  5       nU(       d  [        SS5      eUS   S:X  a5  U R                  SU45      R                  5       S   S::  a  [        S	S
5      eg g )Nz>SELECT role FROM memberships WHERE business_id=? AND user_id=?rÚ   úTeam member not found.r¬   rª   zESELECT COUNT(*) FROM memberships WHERE business_id=? AND role='owner'r   r   r_   z1Add another owner before changing the last owner.©r€   r‡   r
   )r�   rØ   rø   r<   s       r>   Úguard_last_ownerr  C  s’   € Ø�‰ÐUÐWZÐV_Ó`×iÑiÓk€CÞÜ˜CÐ 8Ó9Ð9Ø
ˆ6�{�GÓ §¡Ð-vÐx{Ðw}Ó ~÷  !Hñ  !Hó  !Jð  KLñ  !Mð  OPó  !PÜ˜CÐ SÓTÐTð !PÐr@   z/api/b/{bid}/team/{rid}c           
      ó�  • [        SS9 n[        R                  " XCU S5        UR                  S:w  a  [	        X@U5        UR                  SX45      R                  5       (       d  [        SS5      eUR                  SUR                  X45        [        XCU SS	XR                  5        S S S 5        S
S0$ ! , (       d  f       S
S0$ = f)NTrŒ   rª   ú;SELECT 1 FROM memberships WHERE business_id=? AND user_id=?rÚ   r  z?UPDATE memberships SET role=? WHERE business_id=? AND user_id=?zChanged team roler™   r   )	r   r   r  r¬   r  r€   r‡   r
   r%   ©rØ   rø   rŠ   r¨   r�   s        r>   Ú	team_roler   K  s´   € ä	�$Š˜4Ü�Š�t  WÔ-Ø�<‰<˜Ó Ü˜T cÔ*Ø�|‰|ÐYÐ[^ÐZcÓd×mÑm×oÑoÜ Ð$<Ó=Ð=Ø�‰ÐVÐX_×XdÑXdÐehÐWmÔnÜˆd˜Ð/°¸¿L¹LÔI÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðús   ŠBB4Â4
Cc           	      ó  • [        SS9 n[        R                  " X2U S5        [        X0U5        UR	                  SX45        UR	                  SX45        [        X2U SSU5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)	NTrŒ   rª   zPUPDATE jobs SET assigned_user_id=NULL WHERE business_id=? AND assigned_user_id=?z9DELETE FROM memberships WHERE business_id=? AND user_id=?zRemoved team memberr™   r   )r   r   r  r  r€   r%   )rØ   rø   r¨   r�   s       r>   Úteam_remover"  X  s�   € ä	�$Š˜4Ü�Š�t  WÔ-Ü˜ #Ô&Ø�‰ÐgÐilÐhqÔrØ�‰ÐPÐRUÐQZÔ[Üˆd˜Ð1°'¸#Ô>÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðús   ŠAA0Á0
Bc                 ó¨   • [        U 5      nS H@  nX!;   d  M
  [        R                  " UR                  U5      5      XR	                  S5      '   MB     U$ )N)Ú
items_jsonÚchecklist_jsonÚ_json)r8   rÃ   Úloadsr9   Úremovesuffix)r<   r=   Úkeys      r>   Ú	serializer*  c  sE   € Ü	ˆc‹€DÛ.ˆØ�;Ü,0¯JªJ°t·x±xÀ³}Ó,EˆD×!Ñ! 'Ó*Ó+ñ /ð €Kr@   c                 óš  • U R                  SU S3U45       Vs/ s H  n[        U5      PM     nnUS:X  aŠ  U R                  SU45       Vs0 s H  o3S   US   _M     nnU H[  nUR                  US   S5      US	'   US
   S:X  a  SO
US   US	   -
  US'   US
   S:X  a  SOUS   S:X  a  SOUS	   (       a  SOSUS'   M]     U$ s  snf s  snf )NzSELECT * FROM z8 WHERE business_id=? ORDER BY created_at DESC,rowid DESCr(   z�SELECT invoice_id,SUM(amount_cents) amount FROM transactions
            WHERE business_id=? AND kind='payment' AND voided=0 GROUP BY invoice_idÚ
invoice_idÚamountr�   r   Ú
paid_centsr~   ÚvoidÚtotal_centsÚbalance_centsÚpaidÚpartialÚunpaidÚpayment_status)r€   r*  rg   )r�   ÚtablerØ   r¯   Úrowsr2  r<   s          r>   Úlist_recordsr8  k  s<  € Ø $§¡¨~¸e¸WÐD|Ð-}ð  @Cð  Eô  !Fó  
Gò  !F˜1Œ)�AŽ,ñ  !F€Dð  
GØˆjÓØ37·<±<ð AWØX[ÐW]ô4_ó `ò 4_¨a�‰o˜a ™kÒ)ñ 4_ˆð `ãˆCØ"Ÿh™h s¨4¡y°Ó3ˆC�ÑØ&)¨(¡m°VÓ&;¡ÀÀ]ÑASÐTWÐXdÑTeÑAeˆC�Ñ Ø,/°©M¸6Ó,A¡&ÐRUÐVeÑRfÐhiÓRiÉð  ~Að  BN÷  ~OÑpyð  U]ˆCÐ Ó!ñ ð €Kùò 
Gùò`s   šCÁCc                 ó€   • U R                  SU45      R                  5       nU(       d  [        SS5      e[        U5      $ )Nú#SELECT * FROM businesses WHERE id=?rÚ   rÛ   )r€   r‡   r
   r8   )r�   rØ   r<   s      r>   Ú_email_settings_rowr;  w  s9   € Ø�‰Ð:¸C¸6ÓB×KÑKÓM€CÞÜ˜CÐ 5Ó6Ð6Ü�‹9Ðr@   z/api/b/{bid}/email-settingsc                 óp  • [        5        n[        R                  " X!U S5        [        X 5      nUR	                  SS5      UR	                  SS5      UR	                  SS5      UR	                  SS5      UR	                  SS	5      UR	                  S
S5      UR	                  SS5      [        UR	                  S5      5      [        UR	                  S5      5      UR	                  SS5      =(       d+    UR	                  SS5      =(       d    UR	                  SS5      S.
sS S S 5        $ ! , (       d  f       g = f)Nrª   Úemail_sender_namerV   Úsmtp_from_emailÚsmtp_reply_toÚ	smtp_hostzsmtp.purelymail.comÚ	smtp_portiÑ  Úsmtp_securityÚsslÚsmtp_usernameÚsmtp_enabledr6   Úapproval_notify_emailr‘   )
Úsender_nameÚ
from_emailÚreply_tor@  rA  rB  rD  ÚenabledÚpassword_setrF  )r   r   r  r;  rg   r;   )rØ   r¨   r�   r<   s       r>   Úemail_settings_getrL  ~  sï   € ä	Œ�Ü�Š�t  WÔ-Ü Ó)ˆàŸ'™'Ð"5°bÓ9ÈÏÉÐPaÐbdÓHeØ—w‘w˜¨rÓ2ÀÇÁÈÐTiÓ@jØŸ™ ¨CÓ0À#Ç'Á'È/ÐZ_ÓB`ØŸG™G O°BÓ7Ä4ÈÏÉÐP^ÓH_ÓC`Ü §¡Ð(;Ó <Ó=Ø$'§G¡GÐ,CÀBÓ$G×$È3Ï7É7ÐSdÐegÓKh×$Ðlo×lsÑlsÐt{Ð|~Ólñ
÷ 
��ús   ‹DD'Ä'
D5c                 ó˜  • [        SS9 n[        R                  " X2U S5        [        X05      nUR	                  SS5      nUR
                  (       a  [        UR
                  5      nUR                  (       a  U(       d  [        SS5      eUR                  SUR                  UR                  UR                  UR                  UR                  UR                  UR                   U[#        UR                  5      UR$                  ['        5       U 45        [)        X2U S	S
XR                  5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   rª   r6   rV   r_   z2Enter the SMTP app password before enabling email.zãUPDATE businesses SET email_sender_name=?,smtp_from_email=?,smtp_reply_to=?,smtp_host=?,smtp_port=?,
            smtp_security=?,smtp_username=?,smtp_password_enc=?,smtp_enabled=?,approval_notify_email=?,updated_at=? WHERE id=?zUpdated company email settingsr˜   r   )r   r   r  r;  rg   Úsmtp_passwordr   rJ  r
   r€   rG  rH  rI  r@  rA  rB  rD  rm   rF  r!   r%   )rØ   rŠ   r¨   r�   r<   Úpassword_encs         r>   Úemail_settings_putrP  �  s  € ä	�$Š˜4Ü�Š�t  WÔ-Ü Ó)ˆØ—W‘WÐ0°Ó4ˆØ× × Ü'¨×(=Ñ(=Ó>ˆLØ�?�?¦<Ü Ð$XÓYÐYØ�‰ð Bà× Ñ  ×!3Ñ!3°G×4DÑ4DÀW×EVÑEVÐW^×WhÑWhÐip×i~Ñi~Ø×"Ñ" <´°G·O±OÓ0DÀW×EbÑEbÔcfÓchÐilðnô	oô 	ˆd˜Ð<¸\È#×N`ÑN`Ôa÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðús   ŠD"D8Ä8
E	z /api/b/{bid}/email-settings/testc           
      óÆ  • [        5        n[        R                  " X2U S5        [        X05      nS S S 5         [	        WUR
                  SSUR                  SS5       S3S9  [        SS9 n[        X2U SSXR
                  5        S S S 5        SS0$ ! , (       d  f       Nh= f! [         a   n[        SS	[        U5      S S
  35      eS nAff = f! , (       d  f       SS0$ = f)Nrª   zLB CRM email testzThis is a test message from r�   zyour companyz& CRM. Your SMTP connection is working.©Úto_emailÚsubjectÚtextéö  zEmail test failed: é^  TrŒ   zSent test emailr˜   r   )r   r   r  r;  r   rS  rg   Ú	Exceptionr
   Ústrr%   )rØ   rŠ   r¨   r�   r  r{   s         r>   Úemail_settings_testrZ  Ÿ  sà   € ä	Œ�Ü�Š�t  WÔ-Ü$ TÓ.ˆ÷ 
ðHÜ�X w×'7Ñ'7Ð@SØ8¸¿¹ÀfÈ^Ó9\Ð8]ð  ^Dð  Eò	Fô 
�$Š˜4Üˆd˜Ð-¨l¸3×?OÑ?OÔP÷ 
à�ˆ;Ð÷ 
�ûô ó HÜ˜CÐ"5´c¸#³h¸tÀ°nÐ5EÐ FÓGÐGûðHúç	Œà�ˆ;Ðús.   ‹$B¸*B" Á+CÂ
BÂ"
CÂ,CÃCÃ
C Úreturnc                 ó@  • [         R                  " SS5      R                  5       R                  S5      nU(       a  U$ U R                  R                  S5      =(       d    U R                  R                  =(       d    SR                  S5      S   R                  5       nU R                  R                  S5      =(       d8    U R                  R                  S	5      =(       d    U R                  R                  R                  S5      S   R                  5       nU S
U 3$ )NrU   rV   rW   zx-forwarded-protorO   rM   r   zx-forwarded-hostrY   rX   )
rh   ri   r    rj   rf   rg   rk   rl   ÚsplitÚnetloc)rr   r…   ÚprotorY   s       r>   Ú_public_originr`  ®  sÛ   € Ü�yŠyÐ,¨RÓ0×6Ñ6Ó8×?Ñ?ÀÓD€JÞØÐØ�?‰?×ÑÐ2Ó3×S°w·{±{×7IÑ7I×SÈV×
ZÑ
ZÐ[^Ó
_Ð`aÑ
b×
hÑ
hÓ
j€EØ
�/‰/×
Ñ
Ð0Ó
1×
f°W·_±_×5HÑ5HÈÓ5P×
fÐT[×T_ÑT_×TfÑTf×	mÑ	mÐnqÓ	rÐstÑ	u×	{Ñ	{Ó	}€DØˆW�C˜�vÐÐr@   Útokenc                 óf   • [         R                  " U R                  5       5      R                  5       $ rB   )ÚhashlibÚsha256ÚencodeÚ	hexdigest)ra  s    r>   Ú_token_hashrg  ·  s    € Ü�>Š>˜%Ÿ,™,›.Ó)×3Ñ3Ó5Ð5r@   c                 ó¤  ^	• SS K m	SR                  U	4S jUR                  S/ 5       5       5      nU(       a%  ST	R                  UR                  SS5      5       S3OSnST	R                  US	S
9 S3nST	R                  U5      R	                  [        S5      S5       SU ST	R                  U R                  SS5      5       ST	R                  UR                  SS5      5       ST	R                  UR                  SS5      5       ST	R                  UR                  SS5      5       SU SU S[        UR                  SS5      5      S-  S S3$ )Nr   rV   c              3   ól  >#   • U  H©  nS TR                  [        UR                  SS5      5      5       STR                  [        UR                  SS5      5      5       STR                  [        UR                  SS5      5      5       S[        UR                  SS	5      5      S
-  S S3	v •  M«     g7f)z<<tr><td style='padding:8px;border-bottom:1px solid #e5e7eb'>ÚdescriptionrV   zN</td><td style='padding:8px;border-bottom:1px solid #e5e7eb;text-align:right'>ÚquantityÚ ÚunitzO</td><td style='padding:8px;border-bottom:1px solid #e5e7eb;text-align:right'>$r0  r   éd   ú,.2fú
</td></tr>N)ÚescaperY  rg   rm   )rÿ   ÚiÚhtmls     €r>   r  Ú'_estimate_email_html.<locals>.<genexpr>½  sw  øé € ð  ~ò  e}ð  `aÐTÐUY×U`ÑU`ÔadÐef×ejÑejÐkxÐy{Óe|Óa}ÓU~ÐTð  @Nð  OS÷  OZñ  OZô  [^ð  _`÷  _dñ  _dð  eoð  pró  _só  [tó  Ouð  Nvð  vwð  x|÷  xCñ  xCô  DGð  HI÷  HMñ  HMð  NTð  UWó  HXó  DYó  xZð  w[ð  [jô  knð  op÷  otñ  otð  uBð  CDó  oEó  kFð  GJñ  kJð  KOð  jPð  PZõ  [ò  e}ùs   ƒB1B4Úitemsz<p><b>Property:</b> r“   z</p>z"<p style='margin:28px 0'><a href='T©Úquotezª' style='display:inline-block;background:#2563eb;color:white;text-decoration:none;padding:14px 22px;border-radius:8px;font-weight:700'>View &amp; approve estimate</a></p>zK<div style='font-family:Arial,sans-serif;max-width:720px;color:#1f2937'><p>é
   z<br>z6<hr style='border:0;border-top:1px solid #e5e7eb'><h2>r�   u    â€” Estimate Úcodez</h2><p><b>rG   z</b></p><p>Prepared for: a  <table style='width:100%;border-collapse:collapse'><tr><th style='text-align:left;padding:8px;background:#f3f6fa'>Description</th><th style='text-align:right;padding:8px;background:#f3f6fa'>Qty</th><th style='text-align:right;padding:8px;background:#f3f6fa'>Total</th></tr>z></table><p style='text-align:right;font-size:20px'><b>Total: $r0  rn  ro  z{</b></p><p>The PDF estimate is attached. You can also use the secure button above to approve the estimate online.</p></div>)rs  r  rg   rq  ÚreplaceÚchrrm   )
rš   ÚcustomerÚestimateÚproperty_rowrº   Úapproval_urlÚ	rows_htmlÚpropÚbuttonrs  s
            @r>   Ú_estimate_email_htmlrƒ  »  sÎ  ø€ ÛØ�g‰gô  ~ð  em÷  eqñ  eqð  ryð  z|ô  e}ó  ~ó  ~€IÞUaÐ §¡¨L×,<Ñ,<¸YÀrÓ,JÓ KÐLÈDÑ	QÐgi€DØ/°·±¸LÐPT°Ð0UÐ/Vð  WAð  B€FØZÐ[_×[fÑ[fÐgnÓ[o×[wÑ[wÔx{Ð|~Óxð  AGó  \Hð  [Ið  IMð  NTð  MUð  UKð  LP÷  LWñ  LWð  X`÷  Xdñ  Xdð  ekð  lnó  Xoó  Lpð  Kqð  qð  @D÷  @Kñ  @Kð  LT÷  LXñ  LXð  Y_ð  `bó  Lcó  @dð  eð  epð  qu÷  q|ñ  q|ð  }E÷  }Iñ  }Ið  JQð  RTó  }Uó  qVð  pWð  Wpð  qu÷  q|ñ  q|ð  }E÷  }Iñ  }Ið  JPð  QSó  }Tó  qUð  pVð  VZð  [_ð  Z`ð  `q
ð  r
{
ð  q
|
ð  |
zô  {~ð  G÷  Kñ  Kð  LYð  Z[ó  \ó  {]ð  ^añ  {að  bfð  zgð  gbð  eð  er@   z"/api/b/{bid}/estimates/{rid}/emailc                 ó€  • [         R                  " S5      n[        U5      n[        SS9 n[        R
                  " XtU S5        [        [        USX5      5      nUS   S;   a  [        SS	5      e[        US
US   U 5      n	UR                  S5      (       a  [        USUS   U 5      OS n
[        Xp5      nUR                  SU[        5       [        5       X45        S S S 5        [        U5       SU 3n [        WW	WW
5      n[        X¹XŠUR                   U5      nUR                   R#                  5       SU S3-   n[%        X²R&                  UR(                  XþXØS    S3S9  [        SS9 nUS   S:X  a  [/        USXSS05        [1        XtU SSXS    SUR&                   S35        S S S 5        SUS   S:H  SS.$ ! , (       d  f       Nå= f! [*         aX  n[        SS9 nUR                  SXU45        S S S 5        O! , (       d  f       O= f[        SS[-        U5      S S  35      eS nAff = f! , (       d  f       N�= f)Né    TrŒ   Úmanager*   r~   ©r/  Údeclinedr_   z/Voided or declined estimates cannot be emailed.r1   Úcustomer_idÚproperty_idr2   z…UPDATE estimates SET public_token_hash=?,public_token_issued_at=?,public_token_used_at=NULL,updated_at=? WHERE id=? AND business_id=?rb   z"

View and approve your estimate: Ú
ry  z.pdf)rS  rT  rU  rs  Ú
attachmentÚattachment_namezzUPDATE estimates SET public_token_hash='',public_token_issued_at=NULL WHERE id=? AND business_id=? AND public_token_hash=?rV  zCould not send estimate email: rW  ÚdraftÚsentzEmailed estimatez to z; online approval enabled)r   Úmarked_sentÚonline_approval)rð   rñ   rg  r   r   r  r*  r$   r
   rg   r;  r€   r!   r`  r   rƒ  rº   rj   r   rS  rT  rX  rY  r#   r%   )rØ   rø   rŠ   rr   r¨   Ú	raw_tokenÚ
token_hashr�   Úestr|  r�  r  r  ÚpdfÚ	html_bodyÚ	text_bodyr{   s                    r>   Úestimate_emailr˜  Ã  sB  € ä×#Ò# BÓ'€IÜ˜9Ó%€JÜ	�$Š˜4Ü�Š�t  XÔ.Ü”f˜T +¨cÓ6Ó7ˆØˆx‰=Ð/Ó/Ü Ð$UÓVÐVÜ˜˜[¨¨]Ñ);¸CÓ@ˆØADÇÁÈ×AWÑAWŒV�D˜ c¨-Ñ&8¸Ô=Ð]aˆÜ$ TÓ.ˆØ�‰ð  ]Ø ¤£¤s£u¨SÐ5ô	7÷ 
ô # 7Ó+Ð,¨G°I°;Ð?€Lð
TÜ˜x¨°°TÓ:ˆÜ& x¸À'Ç/Á/ÐR^Ó_ˆ	Ø—?‘?×)Ñ)Ó+Ð.RÐS_ÐR`Ð`bÐ,cÑcˆ	Ü�X×'7Ñ'7ÀÇÁÐU^Ø #¸6±{°mÀ4Ð4Hò	Jô 
�$Š˜4Øˆx‰=˜'Ó!Ü�4˜ C¨X°fÐ,=Ô>Üˆd˜Ð.¨{¸3ÀfÁ+ÀÈdÐSZ×ScÑScÐRdÐd}Ð?~Ô÷ 
ð  C¨¡M°7Ñ$:ÈTÑRÐR÷5 
�ûô" ó TÜ�dŠ^˜tØ�L‰Lð  VØ *Ð-ô/÷ �^Ž^úô ˜CÐ"AÄ#ÀcÃ(È4ÈCÀ.ÐAQÐ RÓSÐSûð	Tú÷
 
�úsI   «B/F9Ã3A-G
 Å)<H/Æ9
GÇ

H,Ç	H'ÇG;Ç2	H'Ç;
H		È"H'È'H,È/
H=c                 ó¬  • [        U5      nU R                  SU45      R                  5       nU(       d  g [        U5      n0 n[        R
                  " UR                  S5      =(       d    S5       HÜ  nUR                  S5      nU(       a  Xu;   a  M"  U R                  SUS   U45      R                  5       nU(       d  MP  [        U5      n	S n
U	R                  S5      (       a%  U R                  SUS   U45      R                  5       n
U	R                  S	5      =(       d    S
U	R                  S5      =(       d    S
U
(       a  U
S   OS
S.XW'   MÞ     XTS'   U$ )NaÀ  SELECT e.*,b.name business_name,b.phone business_phone,b.email business_email,b.smtp_from_email,
        c.name customer_name,c.email customer_email,p.address property_address
        FROM estimates e JOIN businesses b ON b.id=e.business_id
        JOIN customers c ON c.id=e.customer_id AND c.business_id=e.business_id
        LEFT JOIN properties p ON p.id=e.property_id AND p.business_id=e.business_id
        WHERE e.public_token_hash=? LIMIT 1r$  ú[]Úinventory_idzzSELECT id,name,customer_description,show_image_quote FROM inventory_items
            WHERE business_id=? AND id=? LIMIT 1rì   Úshow_image_quotezÝSELECT id,content_type,filename FROM attachments
                WHERE business_id=? AND entity='inventory_items' AND entity_id=? AND content_type LIKE 'image/%'
                ORDER BY created_at DESC,rowid DESC LIMIT 1r�   rV   Úcustomer_descriptionr�   )r�   r�  Úimage_attachment_idÚ_public_item_meta)rg  r€   r‡   r8   rÃ   r'  rg   )r�   ra  r“  r<   ÚresultÚ	item_metaÚitemr›  ÚinvÚmetarŒ  s              r>   Ú_public_quote_lookupr¥  ä  sO  € Ü˜5Ó!€JØ�‰ð /ð
 1;¨}ó	>÷
 ?G¹h»jð ö ØÜ�‹9€Fð €IÜ—
’
˜6Ÿ:™: lÓ3×;°tÖ<ˆØ—X‘X˜nÓ-ˆÞ˜|Ó8ÙØ�L‰Lð 4Ø5;¸MÑ5JÈ<Ð4XóZßZbÑZbÓZdð 	æÙÜ�#‹YˆØˆ
Ø�8‰8Ð&×'Ñ'Ø—|‘|ð %?à@FÀ}Ñ@UÐVbÐ?cóeçemÑemÓeoð ð —8‘8˜FÓ#×) rØ#'§8¡8Ð,BÓ#C×#IÀrÞ6@ *¨TÒ"2Àbñ!
ˆ	Óñ =ð& !*ÐÑØ€Mr@   c                 ó–  • SS K nU (       d
  [        SSS9$ [        R                  " U R	                  S5      =(       d    S5      nU R	                  S5      =(       d    0 n/ nU GH×  n	U	R	                  S5      =(       d    S	n
UR	                  U
0 5      nUR	                  S
5      =(       d    S	nU(       ao  SUR                  USS9 SUR                  USS9 SUR                  [        UR	                  S5      =(       d    U	R	                  S5      =(       d    S5      SS9 S3OS	n[        UR	                  S5      =(       d    S	5      R                  5       n[        U	R	                  SS	5      5      nSUR                  U5       S3U(       a  Xï:w  a  SUR                  U5       S3OS	-   S-   nUR                  SU U SUR                  [        U	R	                  SS	5      5      5       SUR                  [        U	R	                  SS	5      5      5       S[        U	R	                  S S5      5      S!-  S" S#3
5        GMÚ     S	R                  U5      n[        U R	                  S$5      =(       a)    U S$   [        R                  " 5       R                  5       :  5      n[        U R	                  S%5      =(       d    U R	                  S&5      S':H  5      nU=(       d    U R	                  S&5      S(;   nU=(       d    U(       a  S)O
U(       a  S*OS+nS	nU(       d_  U(       dX  S,UR                  USS9 S-UR                  U R	                  S.S	5      SS9 S/UR                  U R	                  S0S15      5       S23nO	U(       a  S3nU(       d  U(       a  S4O
U(       a  S5OS	nU R	                  S65      (       a'  S7UR                  U R	                  S6S	5      5      -   S-   OS	nU R	                  S85      (       a$  S9UR                  U R	                  S8S	5      5      -   OS	nS	R                  / S:PUR                  U R	                  S;S<5      5       PS=PUR                  U R	                  S0S	5      5       PS>PUR                  U R	                  S0S	5      5       PS?PUR                  U R	                  S;S<5      5       PS9PUR                  U R	                  S@S	5      5       PSAPUR                  U R	                  S.S	5      5       PSBPU PSCPUR                  U5       PSDPUR                  U R	                  SE5      =(       d    SF5       PSGPUR                  U R	                  S$5      =(       d    SH5       PSIPU PSJP[        U R	                  S S5      5      S!-  S" PSPU PU PSKPUR                  U R	                  S0S	5      5       PU PSLP5      n[        UUS9$ )MNr   zÕ<main style='font-family:Arial,sans-serif;max-width:720px;margin:60px auto;padding:24px'><h1>Estimate link unavailable</h1><p>This approval link is invalid or has been replaced by a newer estimate link.</p></main>rÚ   r[   r$  rš  rŸ  r›  rV   rž  z'<img class='product-image' src='/quote/Trv  z/image/z' alt='r�   rj  ÚProductz'>r�  z"<div class='product-copy'><strong>z	</strong>z<span>z</span>z</div>z"<tr><td><div class='product-cell'>z</div></td><td class='num'>rk  rl  rm  z</td><td class='num'>$r0  rn  ro  rp  Ú
expires_onÚpublic_token_used_atr~   Úacceptedr‡  ú(This estimate has already been accepted.úJThis estimate has expired. Please contact the office for an updated quote.z?Review the scope and pricing below, then confirm your approval.z#<form method="post" action="/quote/zu/accept" class="approve-form"><label>Your name<input name="name" required maxlength="200" autocomplete="name" value="Úcustomer_namez†"></label><label class="agree"><input type="checkbox" name="agree" value="yes" required> <span>I agree to this estimate and authorize rî   zthe companyzi to perform the work described above.</span></label><button type="submit">Accept estimate</button></form>u^   <div class='accepted'>âœ“ Estimate accepted. Thank you â€” the office has been notified.</div>ÚsuccessÚwarnÚnoteszD<div class='note'><strong>Scope, terms &amp; exclusions</strong><br>Úbusiness_phoneu    Â· ze<!doctype html><html><head><meta name="viewport" content="width=device-width,initial-scale=1"><title>ry  ÚEstimateu    â€” a´	  </title><style>*{box-sizing:border-box}body{margin:0;background:#f4f7fb;color:#14213d;font-family:Arial,sans-serif}main{max-width:820px;margin:32px auto;padding:0 18px}.card{background:white;border:1px solid #dce3ee;border-radius:16px;overflow:hidden;box-shadow:0 10px 28px rgba(20,33,61,.07)}header{padding:28px 30px;border-bottom:1px solid #e5eaf2}header h1{margin:0 0 7px;font-size:26px}.muted{color:#66758f}section{padding:26px 30px}.meta{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin:16px 0}.meta div{background:#f7f9fc;padding:14px;border-radius:10px}table{width:100%;border-collapse:collapse;margin:18px 0}th,td{padding:12px 8px;border-bottom:1px solid #e7ebf1;text-align:left}th{font-size:12px;text-transform:uppercase;color:#718096}.num{text-align:right}.product-cell{display:flex;align-items:center;gap:14px;min-width:0}.product-image{width:76px;height:76px;object-fit:cover;border-radius:10px;border:1px solid #dce3ee;background:#f7f9fc;flex:0 0 auto}.product-copy{display:flex;flex-direction:column;gap:5px;min-width:0}.product-copy strong{font-weight:600;color:#14213d}.product-copy span{font-size:13px;line-height:1.4;color:#66758f}.total{font-size:24px;text-align:right;font-weight:700;margin:20px 0}.note{white-space:pre-wrap;background:#f7f9fc;padding:16px;border-radius:10px}.banner{padding:14px 16px;border-radius:10px;background:#eef4ff;margin-bottom:18px}.banner.success,.accepted{background:#e9f8f1;color:#176b4d}.banner.warn{background:#fff4e5;color:#8a5a00}.approve-form{margin-top:24px;border-top:1px solid #e7ebf1;padding-top:24px}label{display:block;font-weight:700;margin:12px 0}input[name=name]{width:100%;margin-top:8px;padding:13px;border:1px solid #cfd8e6;border-radius:9px;font-size:16px}.agree{display:flex;gap:10px;align-items:flex-start;font-weight:400;line-height:1.45}.agree input{margin-top:4px;min-width:18px;min-height:18px}button{border:0;background:#2563eb;color:white;font-weight:700;padding:14px 22px;border-radius:9px;font-size:16px;cursor:pointer}.accepted{padding:16px;border-radius:10px;font-weight:700;margin-top:22px}footer{padding:18px 30px;background:#f7f9fc;color:#66758f;font-size:13px}@media(max-width:600px){main{margin:12px auto;padding:0 10px}header,section,footer{padding:20px}.meta{grid-template-columns:1fr}th:nth-child(2),td:nth-child(2){display:none}.product-image{width:64px;height:64px}.product-cell{align-items:flex-start;gap:10px}}</style></head><body><main><div class="card"><header><div class="muted">z
</div><h1>rG   z%</h1><div class="muted">Prepared for z+</div></header><section><div class="banner z">z:</div><div class="meta"><div><strong>Property</strong><br>Úproperty_addresszNot specifiedz-</div><div><strong>Valid through</strong><br>zNo expiration datezx</div></div><table><thead><tr><th>Description</th><th class="num">Qty</th><th class="num">Total</th></tr></thead><tbody>z+</tbody></table><div class="total">Total: $z%</section><footer>Questions? Contact z$</footer></div></main></body></html>)rs  r   rÃ   r'  rg   rq  rY  r    rà   rm   r  r;   r   ÚtodayÚ	isoformat)Úqra  rº   r®  r\   rs  ru  r¡  Ú	row_partsrr  r›  r¤  Úattachment_idÚ
image_htmlr�  rj  Údetail_htmlr7  ÚexpiredÚalreadyÚdisabledÚnotice_textÚformÚstatus_classÚ
notes_htmlÚ
phone_htmlÚbodys                              r>   Ú_public_quote_pagerÄ  
  s2	  € ÛÞÜð  tð  ADñ  Eð  	EÜ
�*Š*�Q—U‘U˜<Ó(×0¨DÓ
1€EØ�e‰eÐ'Ó(×.¨B€IØ€IÜˆØ—U‘U˜>Ó*×0¨bˆØ�]‰]˜<¨Ó+ˆØ—h‘hÐ4Ó5×;¸ˆö xEð >¸d¿k¹kÈ%ÐVZ¸kÐ>[Ð=\Ð\cÐdh×doÑdoÐp}ð  EIÐdoð  eJð  dKð KØ ŸK™K¬¨D¯H©H°VÓ,<×,aÀÇÁÀmÓ@T×,aÐXaÓ(bÐim˜KÐnÐoÐoqñsð KMð 	ä  §¡Ð*@Ó!A×!GÀRÓH×NÑNÓPÐÜ˜Ÿ™˜m¨BÓ/Ó0ˆØ9¸$¿+¹+ÀkÓ:RÐ9SÐS\Ð]ÞOcÐh|ó  iL˜& §¡Ð-AÓ!BÐ CÀ7ÑKð  RTñUàñ ˆð 	×ÑÐ=¸j¸\È+Èð W,Ø,0¯K©K¼¸A¿E¹EÀ*ÈRÓ<PÓ8QÓ,RÐ+SÐSTÐUY×U`ÑU`ÔadÐef×ejÑejÐkqÐrtÓeuÓavÓUwÐTxð y-Ü-0°·±°}ÀQÓ1GÓ-HÈÑ-LÈTÐ,RÐR\ð^÷ 	_ñ ð 
�‰�Ó	€DÜ�—‘�|Ó$×S¨¨<©¼4¿:º:»<×;QÑ;QÓ;SÑ)SÓT€GÜ�—‘Ð-Ó.×M°!·%±%¸³/À:Ñ2MÓN€GØ×>˜Ÿ™˜h›Ð+>Ñ>€HØ÷  yÎ'ÑFö  ipñ  Yeð  vw€KØ	€DÞž8Ø4°T·[±[ÀÈT°[Ð5RÐ4Sð  TIð  JN÷  JUñ  JUð  VW÷  V[ñ  V[ð  \kð  lnó  Voð  vzð  JUð  J{ð  I|ð  |Bð  CG÷  CNñ  CNð  OP÷  OTñ  OTð  Udð  eró  Osó  Ctð  Buð  u^ð  a‰Þ	ØmˆÞ%®‘Æ±vÈr€Lð DE÷  DIñ  DIð  JQ÷  DRñ  DRÐVÐW[×WbÑWbÐcd×chÑchÐipÐqsÓctÓWuÑuÐv~Ò~ð  XZ€JØCDÇ5Á5ÐIY×CZÑCZ��t—{‘{ 1§5¡5Ð)9¸"Ó#=Ó>Ò>Ð`b€J÷ 
k8ó  
k8Ðrð  
k8Ðsw×s~Ñs~ð  @A÷  @Eñ  @Eð  FLð  MWó  @Xó  tYð  sZð  
k8ð  Z_ð  
k8ð  `d÷  `kñ  `kð  lm÷  lqñ  lqð  rAð  BDó  lEó  `Fð  _Gð  
k8ð  GI+ð  
k8ð  J+N+÷  J+U+ñ  J+U+ð  V+W+÷  V+[+ñ  V+[+ð  \+k+ð  l+n+ó  V+o+ó  J+p+ð  I+q+ð  
k8ð  q+{+ð  
k8ð  |+@,÷  |+G,ñ  |+G,ð  H,I,÷  H,M,ñ  H,M,ð  N,T,ð  U,_,ó  H,`,ó  |+a,ð  {+b,ð  
k8ð  b,f,ð  
k8ð  g,k,÷  g,r,ñ  g,r,ð  s,t,÷  s,x,ñ  s,x,ð  y,@-ð  A-C-ó  s,D-ó  g,E-ð  f,F-ð  
k8ð  F-k-ð  
k8ð  l-p-÷  l-w-ñ  l-w-ð  x-y-÷  x-}-ñ  x-}-ð  ~-M.ð  N.P.ó  x-Q.ó  l-R.ð  k-S.ð  
k8ð  S.~.ð  
k8ð  .K/ð  ~.L/ð  
k8ð  L/N/ð  
k8ð  O/S/÷  O/Z/ñ  O/Z/ð  [/f/ó  O/g/ð  N/h/ð  
k8ð  h/b0ð  
k8ð  c0g0÷  c0n0ñ  c0n0ð  o0p0÷  o0t0ñ  o0t0ð  u0G1ó  o0H1÷  o0[1ð  L1[1ó  c0\1ð  b0]1ð  
k8ð  ]1J2ð  
k8ð  K2O2÷  K2V2ñ  K2V2ð  W2X2÷  W2\2ñ  W2\2ð  ]2i2ó  W2j2÷  W2B3ð  n2B3ó  K2C3ð  J2D3ð  
k8ð  D3|4ð  
k8ð  }4A5ð  |4B5ð  
k8ð  B5m5ð  
k8ô  n5q5ð  r5s5÷  r5w5ñ  r5w5ð  x5E6ð  F6G6ó  r5H6ó  n5I6ð  J6M6ñ  n5M6ð  N6R6ð  m5S6ð  
k8ð  S6Y6ð  
k8ð  Z6d6ð  Y6e6ð  
k8ð  f6j6ð  e6k6ð  
k8ð  k6P7ð  
k8ð  Q7U7÷  Q7\7ñ  Q7\7ð  ]7^7÷  ]7b7ñ  ]7b7ð  c7r7ð  s7u7ó  ]7v7ó  Q7w7ð  P7x7ð  
k8ð  y7C8ð  x7D8ð  
k8ð  D8h8ô  
k8€DÜ˜¨Ñ5Ð5r@   z$/quote/{token}/image/{attachment_id}r¸  c                 ód  • [        5        n[        X 5      nU(       d  [        SS5      eUR                  S5      =(       d    0 R	                  5        Vs1 s H+  oDR                  S5      (       d  M  UR                  S5      iM-     nnX;  a  [        SS5      eUR                  SXS   45      R                  5       nU(       d  [        SS5      eUS   ovS   nS S S 5        [        S	-  W-  n	U	R                  5       (       d  [        SS5      e[        U	WS
SS.S9$ s  snf ! , (       d  f       NN= f)NrÚ   zEstimate image unavailable.rŸ  rž  z~SELECT storage_key,content_type FROM attachments
            WHERE id=? AND business_id=? AND entity='inventory_items' LIMIT 1rì   Ústorage_keyÚcontent_typeÚuploadsÚinlinezprivate, no-store, max-age=0)úContent-Dispositionrc   ©Ú
media_typerf   )
r   r¥  r
   rg   Úvaluesr€   r‡   r   Úis_filer   )
ra  r¸  r�   r¶  r¤  Úallowed_idsr<   rÆ  rÇ  r  s
             r>   Úpublic_quote_imagerÐ  0  s@  € ä	Œ�Ü
˜tÓ
*ˆÞÜ Ð$AÓBÐBØBCÇ%Á%ÐH[ÓB\×BbÐ`b×AjÑAjÔAló  QÒAl¸×pxÑpxð  zO÷  qPÓ4�T—X‘XÐ3Ö4ÑAlˆð  QØÓ+Ü Ð$AÓBÐBØ�L‰Lð QØR_ÐboÑ`pÐQqósßs{Ñs{Ós}ð 	æÜ Ð$AÓBÐBØ˜Ñ&ˆ¸Ñ5H¨÷ 
ô �IÑ˜kÑ)€FØ�>‰>×ÑÜ˜CÐ =Ó>Ð>Ü˜¨,ÐV^ð  pNñ  @Oñ  Pð  Pùò Q÷	 
�ús%   ‹A	D!ÁDÁ1DÂAD!ÄD!Ä!
D/z/quote/{token})Úresponse_classc                 ó®   • [        U 5      S:”  a  [        SS5      e[        5        n[        X5      nS S S 5        [	        WU 5      $ ! , (       d  f       N= f)NéÈ   rÚ   úEstimate link unavailable.)rô   r
   r   r¥  rÄ  )ra  r�   r¶  s      r>   Úpublic_quoterÕ  D  sF   € ä
ˆ5ƒz�#ƒ~Ü˜CÐ <Ó=Ð=Ü	Œ�Ü
˜tÓ
*ˆ÷ 
ä˜a Ó&Ð&÷ 
�ús   ¦AÁ
Az/quote/{token}/acceptc              ƒ   ó¬  #   • [        U 5      S:”  a  [        SS5      eUR                  5       I S h  v•N n[        UR	                  SS5      5      R                  5       S S n[        UR	                  SS5      5      R                  5       S;   nU(       a  U(       d*  [        5        n[        XP5      nS S S 5        [        WU SS	S
9$ 0 n[        SS9 n[        XP5      nU(       d  [        S U 5      sS S S 5        $ UR	                  S5      (       a?  US   [        R                  " 5       R                  5       :  a  [        X`S5      sS S S 5        $ UR	                  S5      S;   a  [        X`S5      sS S S 5        $ UR	                  S5      (       d  UR	                  S5      S:X  a  [        X`SS5      sS S S 5        $ US   o†S   n	S U S3S.n
[        USX˜[        SSSU 3[        5       [        5       S95        [!        USX˜5      n[#        XXU	5      nU(       a  [%        ['        XXXºU5      5      nO[)        XXXº5      n[+        XXXºU5        [-        XZUSSX–S    SU S UR	                  SS5       S!35        [/        XX5      nXîR	                  S"5      =(       d)    UR	                  S#5      =(       d    UR	                  S$5      US   US%   UR	                  S&5      =(       d    SUS'   UR	                  SS5      U[1        U5      X‰S(.n[        XP5      nS S S 5        UR	                  S)5      (       a€   S*US    S+US,    3nUS,    S-US    S.US/    S0US1    S2US3   S4-  S5 S6US7    S8US9    S:3n[3        US;   US)   UUS<9  [        SS9 n[-        US S=S.US>   S?SUS@   US)   5        S S S 5        [        WU SCS5      $  GN‡! , (       d  f       GN= f! , (       d  f       NÇ= f! , (       d  f       NB= f! [4         aX  n[        SS9 n[-        US S=S.US>   SASUS@   [        U5      S SB 5        S S S 5         S nAM�  ! , (       d  f        S nANŸ= fS nAff = f7f)DNrÓ  rÚ   rÔ  r�   rV   Úagree)ÚyesÚonÚtruerF   z=Enter your name and check the agreement box before accepting.r_   r[   TrŒ   r¨  r¬  r~   r‡  z2This estimate is no longer available for approval.r©  rª  r«  rì   r�   z (customer))r�   r�   r*   zOnline approvalzAccepted online by )r~   Úapproval_methodÚapproval_noteÚaccepted_atr©  z!Customer accepted estimate onlinery  z accepted by z; job z ready to schedulerF  r>  r‘   r­  r³  r0  )r  Útory  r|  ÚpropertyÚtotalÚjob_codeÚaccepted_byrT   rØ   Úestimate_idrÞ  ú	Estimate u    accepted â€” r|  z accepted estimate z online.

Accepted by: râ  z
Property: rß  z
Estimate total: $rà  rn  ro  z
Job created/updated: rá  z

Open the CRM: rT   z/#jobsr  rR  zCRM automationrØ   z%Sent estimate acceptance notificationrã  z Estimate acceptance email failedi„  u>   Estimate accepted. Thank you â€” the office has been notified.)rô   r
   r¿  rY  rg   r    Úlowerr   r¥  rÄ  r   r´  rµ  r#   r8   r!   r$   Újob_for_estimate_lineager*  Úsync_job_from_estimateÚcreate_job_from_estimateÚsync_final_invoicer%   r;  r`  r   rX  )ra  rr   r¿  r­  Úagreedr�   r¶  ÚnotifyrØ   rø   Úactorr”  ÚexistingÚjobr  rT  rU  r{   s                     r>   Úpublic_quote_acceptrï  M  sê  é € ä
ˆ5ƒz�#ƒ~Ü˜CÐ <Ó=Ð=Ø�|‰|‹~×	€DÜ�d—h‘h˜v bÓ)Ó*×0Ñ0Ó2°4°CÐ8€MÜˆt�x‰x˜ Ó#Ó$×*Ñ*Ó,Ð0GÑG€FÞ¦ÜŒT�TÜ" 4Ó.ˆA÷ ä! ! EÐ*iÐvyÑzÐzØ€FÜ	�$Š˜4Ü
˜tÓ
*ˆÞÜ% d¨5Ó1÷ 
‰ð �5‰5�×Ñ 1 \¡?´T·Z²Z³\×5KÑ5KÓ5MÓ#MÜ% aÐ.zÓ{÷ 
‰ð �5‰5�‹?Ð1Ó1Ü% aÐ.bÓc÷ 
‰ð �5‰5Ð'×(Ñ(¨A¯E©E°(«O¸ZÓ,GÜ% aÐ.XÐY]Ó^÷ 
‰ð ˆmÑˆ D¡'˜cØ = /°Ð =Ñ>ˆÜˆt�K ¬°JÐO`ð  rEð  FSð  ETð  pUô  beó  bgô  }@ó  }Bñ  )Cô  	DÜ�4˜ CÓ,ˆÜ)¨$°3Ó7ˆÞÜÔ0°¸#ÀHÓMÓN‰Cä(¨°#Ó<ˆCÜ˜4 C¨cÔ2Üˆd˜Ð@ÀÈSÐV\ÑT]ÐS^Ð^kÐlyÐkzð  {Að  BE÷  BIñ  BIð  JPð  QSó  BTð  AUð  Ugð  Rhô  	iÜ$ TÓ.ˆØ#¯©Ð6MÓ)N÷  *KÐRZ×R^ÑR^Ð_pÓRq÷  *KÐu}÷  vBñ  vBð  CJó  vKð  STð  U[ñ  S\ð  hið  jyñ  hzð  FG÷  FKñ  FKð  L^ó  F_÷  Feð  ceð  noð  p}ñ  n~ð  JM÷  JQñ  JQð  RXð  Y[ó  J\ð  kxô  BPð  QXó  BYð  `cñ  vˆÜ
˜tÓ
*ˆ÷/ 
ð0 ‡z�z�$×Ñð	bØ  v¡Ð/¨~¸fÀZÑ>PÐ=QÐRˆGØ˜JÑ'Ð(Ð(;¸FÀ6¹NÐ;KÐKdÐekÐlyÑezÐd{ð  |Hð  IOð  PZñ  I[ð  H\ð  \oð  pvð  w~ñ  pð  @Cñ  pCð  DHð  oIð  I`ð  agð  hrñ  asð  `tð  tFð  GMð  NVñ  GWð  FXð  X^ð  _ˆDÜ˜ 
Ñ+°V¸D±\È'ÐW[Ò\Ü˜$’ 4Ü�d Ð-=Ñ>¸vÀe¹}ÐMtð  vAð  BHð  IVñ  BWð  X^ð  _cñ  Xdô  e÷  ô
 ˜a Ð&fÐgkÓlÐlòU 
÷ ŽTú÷ 
�ú÷:  •ûäó 	bÜ˜$’ 4Ü�d Ð-=Ñ>¸vÀe¹}ÐMoÐp{ð  }Cð  DQñ  }Rô  SVð  WZó  S[ð  \`ð  ]`ð  Saô  b÷  ———þð	büså   ‚/Q±N8²A4QÂ&N;Â2QÃOÃ/
QÃ9AOÅ
QÅ OÅ.
QÅ87OÆ/
QÆ9EOÌQÌ+AO/ ÎOÎ"O/ Î*QÎ;
O
ÏQÏ
OÏQÏ
O,Ï(O/ Ï+QÏ,O/ Ï/
QÏ9	QÐ'P7Ð)QÐ1QÐ7
Q		ÑQÑQÑ	QÑQÑQz/api/b/{bid}/datac           
      óŒ  • [        5        n[        R                  " X!U 5      nUS;   a  [        O1 Skn[	        U5       Vs0 s H  oU[        X%U 5      _M     nnUS:X  a  US    H  nUR                  SS 5        M     US:w  a-  UR                  SU 45       Vs/ s H  n[        U5      PM     snO/ US'   UR                  SU 45       Vs/ s H  n[        U5      PM     snUS	'   US	    Vs/ s H  n[        U5      PM     n	nU	 Vs1 s H  oˆS
   iM	     n
nUR                  S5       H'  nUS
   U
;  d  M  U	R                  [        USS95        M)     X–S'   US:w  a0  UR                  SU 45       Vs/ s H  n[        U5      PM     snUS'   S S S 5        U$ s  snf s  snf s  snf s  snf s  snf s  snf ! , (       d  f       W$ = f)N)rª   Úmanager>   r/   r0   r1   r2   r4   Ústaffr/   rã  zUSELECT * FROM audit WHERE business_id=? ORDER BY created_at DESC,rowid DESC LIMIT 100Úactivityz¢SELECT u.id,u.name,u.email,u.active,u.phone,u.job_title,m.role FROM users u
            JOIN memberships m ON m.user_id=u.id WHERE m.business_id=? ORDER BY u.namer  r�   zbSELECT id,name,email,active,phone,job_title FROM users WHERE is_super=1 AND active=1 ORDER BY nameÚplatform_adminr«   Úassignable_usersz‡SELECT id,entity,entity_id,filename,content_type,size,category,created_at FROM attachments WHERE business_id=? ORDER BY created_at DESCÚattachments)
r   r   r  ÚTABLESÚsortedr8  r9   r€   r8   rà   )rØ   r¨   r�   r¬   Útablesr6  r   rî  r¯   Ú
assignablerí  Úsus               r>   Úall_datarü  ~  s  € ä	Œ�Ü�^Š^˜D cÓ*ˆØÐ!4Ó4�vÒ:uˆÜ@FÀvÄÓOÂ°u”l 4¨cÓ2Ò2ÁˆÐOØ�‹=à˜f”~�Ø—‘˜ dÖ+ñ &ð _cð  eló  _l¨T¯\©\ð  ;Rð  TWð  SYô  .Zó  [ò  .Z¨œD žGñ  .Zò  [ð  rtˆˆzÑØ)-¯©ð 7ZØ[^ÐZ`ô*bó cò *b Aœ˜Qžñ *bñ cˆˆv‰à%+¨F¢^Ó4¢^ ”D˜–G¡^ˆ
Ð4Ù#-Ó.¢:˜a�D”'¡:ˆÐ.Ø—,‘,ð   Dö  EˆBØ�$‰x˜xÕ'Ø×!Ñ!¤$ rÐ/?Ñ"@ÖAñ Eð $.Ð!Ñ"Ø�‹=Ø48·L±Lð  BKð  MPð  LRô  5Só  #Tò  5S¨q¤4¨¦7ñ  5Sñ  #TˆF�=Ñ!÷% 
ð& €Mùò! Pùò
 [ùòcùâ4ùÚ.ùò #T÷% 
Œð& €Mús`   ‹5F4Á FÁAF4ÂFÂ,F4Ã	F ÃF4Ã+F%Ä F4ÄF*ÄF4Ä5<F4Å1F/ÆF4ÆF4Æ4
Gc                 ór   • [        U SX!5        U(       a#  [        U SX15      nUS   U:w  a  [        SS5      eg g )Nr1   r2   r‰  r_   z2The property must belong to the selected customer.)r$   r
   )r�   rØ   r‰  rŠ  r�  s        r>   Úvalidate_linksrþ  –  sD   € Ü
ˆ4�˜KÔ,ÞÜ�D˜ kÓ6ˆØ�Ñ Ó+Ü Ð$XÓYÐYð ,ð r@   c                 ó   • [        S	[        5       US.UD[        5       [        5       S.D6n[        XU5        [	        XUSUR                  S5      -   XS   [        UR                  SUR                  SS5      5      5      5        [        U5      $ )
N)r�   rì   )r–   r—   zCreated Úsr�   r�   rG   rV   rÓ   )	r8   r    r!   r"   r%   rj   rY  rg   r*  )r�   r6  rØ   r=   r¨   r<   s         r>   Úcreate_basicr  ž  sz   € ÜÐO”“ #ÑO¨ÐO¼»ÌËÓO€CÜ
ˆ4�cÔÜ	ˆ$�C˜
 5§<¡<°Ó#4Ñ4°U¸t¹9ÄSÈÏÉÐRXÐY]×YaÑYaÐbiÐjlÓYmÓInÓEoÔpÜ�S‹>Ðr@   c                 ó    • UR                  5       nUR                  S5      nU(       a%  U(       a  XS:X  a  [        SS5      e[        U SXQ5        U$ )NÚreferred_by_customer_idr_   z#A customer cannot refer themselves.r1   )rÔ   rg   r
   r$   )r�   rØ   rŠ   rø   r=   Úrefs         r>   Úcustomer_datar  ¥  sH   € Ø	×	Ñ	Ó	€DØ�‰Ð*Ó+€CÞ
Þ�3“8Ü Ð$IÓJÐJÜˆt�K Ô(Ø€Kr@   z/api/b/{bid}/customersc                 ó¬   • [        SS9 n[        R                  " X2U S5        [        USU [	        X0U5      U5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   r†  r1   )r   r   r  r  r  rÜ   s       r>   Úcustomer_creater  ¯  s=   € ä	�$Š˜4Ü�Š�t  XÔ.Ü˜D ¨S´¸tÈÓ1PÐQUÓV÷ 
��ús   Š1AÁ
Az/api/b/{bid}/customers/{rid}c                 ó  • [        SS9 n[        R                  " XCU S5        [        USX5        [	        USX[        X@X!5      5        [        XCU SSXR                  5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r1   zUpdated customerr   )r   r   r  r$   r#   r  r%   r�   r  s        r>   Úcustomer_updater	  ¶  su   € ä	�$Š˜4Ü�Š�t  XÔ.´°t¸KÈÔ0PÜˆt�K ¬°d¸wÓ(KÔLÜˆd˜Ð.¨{¸3¿|¹|ÔL÷ 
ð �ˆ;Ð÷	 
Œð �ˆ;Ðús   ŠAA-Á-
A>z/api/b/{bid}/propertiesc                 óâ   • [        SS9 n[        R                  " X2U S5        [        USUR                  U 5        [        USXR                  5       U5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   r†  r1   r2   )r   r   r  r$   r‰  r  rÔ   rÜ   s       r>   Úproperty_creater  ¿  sP   € ä	�$Š˜4Ü�Š�t  XÔ.´°t¸KÈ×H[ÑH[Ð\_Ô0`Ü˜D ¨c×2DÑ2DÓ2FÀtÓL÷ 
��ús   ŠAA Á 
A.z/api/b/{bid}/properties/{rid}c           
      óH  • [        SS9 n[        R                  " XCU S5        [        USX5      nUR                  US   :w  a  [        SS5      e[        USXUR                  5       5        [        XCU SSXR                  5        S S S 5        S	S0$ ! , (       d  f       S	S0$ = f)
NTrŒ   r†  r2   r‰  r_   z<A property cannot be moved between customers after creation.zUpdated propertyr   )
r   r   r  r$   r‰  r
   r#   rÔ   r%   Úlabel©rØ   rø   rŠ   r¨   r�   Úolds         r>   Úproperty_updater  Æ  s›   € ä	�$Š˜4Ü�Š�t  XÔ.Ü�4˜ SÓ-ˆØ×Ñ  MÑ 2Ó2Ü Ð$bÓcÐcÜˆt�L ¨×);Ñ);Ó)=Ô>Üˆd˜Ð.¨|¸CÇÁÔN÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðúó   ŠA:BÂ
B!c                 ó¼   • U R                  SS1S9n[        S0 UD[        R                  " U R                  5      [        R                  " U R
                  5      S.D6$ )NÚpriceÚcostrÑ   )Úprice_centsÚ
cost_centsrÓ   )rÔ   r8   ÚmÚcentsr  r  ©rŠ   r=   s     r>   Úservice_datar  Ò  sI   € Ø	×	Ñ	 W¨VÐ$4Ð	Ð	5€DÜÑ[�$Ð[¤1§7¢7¨7¯=©=Ó#9ÄQÇWÂWÈWÏ\É\ÓEZÓ[Ð[r@   z/api/b/{bid}/servicesc           	      óª   • [        SS9 n[        R                  " X2U S5        [        USU [	        U5      U5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   r†  r)   )r   r   r  r  r  rÜ   s       r>   Úservice_creater  ×  s:   € ä	�$Š˜4Ü�Š�t  XÔ.Ü˜D ¨C´¸WÓ0EÀdÓK÷ 
��úó   Š0AÁ
Az/api/b/{bid}/services/{rid}c           
      ó   • [        SS9 n[        R                  " XCU S5        [        USX5        [	        USX[        U5      5        [        XCU SSXR                  5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r)   zUpdated servicer   )r   r   r  r$   r#   r  r%   r�   r  s        r>   Úservice_updater  Þ  s‚   € ä	�$Š˜4Ü�Š�t  XÔ.´°t¸JÀsÔ0OÜˆt�J˜s¤|°GÓ'<Ô=¼uÀTÈsÐSdÐeoÐps÷  uAñ  uAô  @B÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðúó   ŠAA,Á,
A=c           
      ó  • U R                  1 SkS9n[        S0 UD[        R                  " U R                  5      [        R                  " U R
                  5      [        U R                  5      [        U R                  5      S.D6$ )N>   r  r  Ú	min_stockÚquantity_on_handrÑ   )r  r  r#  r"  rÓ   )	rÔ   r8   r  r  r  r  Úfloatr#  r"  r  s     r>   Úinventory_datar%  æ  sm   € Ø	×	Ñ	Ò$SÐ	Ð	T€DÜñ f�$ð f¤A§G¢G¨G¯M©MÓ$:ÄqÇwÂwÈwÏ|É|ÓG\Ü!& w×'?Ñ'?Ó!@ÌEÐRY×RcÑRcÓLdófð fr@   z/api/b/{bid}/inventoryc           	      óª   • [        SS9 n[        R                  " X2U S5        [        USU [	        U5      U5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   r†  r-   )r   r   r  r  r%  rÜ   s       r>   Úinventory_creater'  ì  s;   € ä	�$Š˜4Ü�Š�t  XÔ.Ü˜DÐ!2°3´~ÀgÓ7NÈtÓT÷ 
��úr  z/api/b/{bid}/inventory/{rid}c           
      ó   • [        SS9 n[        R                  " XCU S5        [        USX5        [	        USX[        U5      5        [        XCU SSXR                  5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r-   zUpdated inventory itemr   )r   r   r  r$   r#   r%  r%   r�   r  s        r>   Úinventory_updater)  ó  sv   € ä	�$Š˜4Ü�Š�t  XÔ.´°tÐ<MÈcÔ0VÜˆtÐ% c¬n¸WÓ.EÔFÜˆd˜Ð4Ð5FÀsÏ<É<ÔX÷ 
ð �ˆ;Ð÷	 
Œð �ˆ;Ðúr   c                 óö   ^• [        U [        5      (       a9  U R                  S5      T:X  a  g[        U4S jU R	                  5        5       5      $ [        U [
        5      (       a  [        U4S jU  5       5      $ g)zNReturn True when a decoded JSON value references this inventory item anywhere.r›  Tc              3   ó<   >#   • U  H  n[        UT5      v •  M     g 7frB   ©Ú_json_contains_inventory©rÿ   Úvr›  s     €r>   r  Ú+_json_contains_inventory.<locals>.<genexpr>  s   øé € ÐUÂnÀÔ+¨A¨|×<Ð<Ânùó   ƒc              3   ó<   >#   • U  H  n[        UT5      v •  M     g 7frB   r,  r.  s     €r>   r  r0    s   øé € ÐLÂeÀÔ+¨A¨|×<Ð<Âeùr1  F)Ú
isinstancer8   rg   ÚanyrÍ  Úlist)Úvaluer›  s    `r>   r-  r-  ü  s\   ø€ ä�%œ×ÑØ�9‰9�^Ó$¨Ó4ØÜÔUÀeÇlÁlÄnÓUÓUÐUÜ�%œ×ÑÜÔLÁeÓLÓLÐLØr@   c                 ó   • / SQn/ nU H€  u  pVn U R                  SU SU S3U45      R                  5       nU HK  n	X–   =(       d    Sn
 [        R
                  " U
5      n[        X²5      (       d  M9  UR                  U5          M~     M‚     U$ ! [        R                   a     M�  f = f! [        [        [        R                  4 a     M’  f = f)N))r*   r$  r}  )r/   r$  rî  )r(   r$  Úinvoice)r.   r$  úpurchase order)r,   r$  úservice call)Úestimate_revisionsÚsnapshot_jsonzestimate revisionz
SELECT id,z FROM z WHERE business_id=?rV   )r€   ÚfetchallÚsqlite3ÚOperationalErrorrÃ   r'  Ú	TypeErrorrn   ÚJSONDecodeErrorr-  rà   )r�   rØ   rø   ÚchecksÚusedr6  Úcolumnr  r7  r<   Úrawr=   s               r>   Ú_inventory_usagerF    sØ   € ò€Fð €DÛ &Ñˆ�uð	Ø—<‘< *¨V¨H°F¸5¸'ÐAUÐ VÐY\ÐX^Ó_×hÑhÓjˆDó ˆCØ‘+×# ˆCðÜ—z’z #“�ô (¨×2Ó2Ø—‘˜EÔ"Úó ñ !'ð €Køô ×'Ñ'ó 	Úð	ûô œz¬4×+?Ñ+?Ð@ó Úðús#   �(BÁB*ÂB'Â&B'Â*CÃCc                 óD  • / n[        SS9 n[        R                  " XBU S5        [        USX5      n[	        X@U5      nU(       a8  SR                  US S 5      [        U5      S:”  a  SOS-   n[        S	S
U S35      eUR                  SU SU45       Vs/ s H  oˆS   PM	     nnUR                  SU SU45        [        XBU SSXS   5        UR                  SX45        S S S 5        U H  n	[        S-  U	-  R                  SS9  M     SS0$ s  snf ! , (       d  f       N9= f)NTrŒ   r†  r-   r  é   z and other recordsrV   rz   z!This inventory item is used on a zB. Deactivate it instead so historical quotes and jobs stay intact.zTSELECT storage_key FROM attachments WHERE business_id=? AND entity=? AND entity_id=?rÆ  zHDELETE FROM attachments WHERE business_id=? AND entity=? AND entity_id=?zDeleted inventory itemr�   z8DELETE FROM inventory_items WHERE id=? AND business_id=?rÈ  ©Ú
missing_okr   )r   r   r  r$   rF  r  rô   r
   r€   r%   r   Úunlink)
rØ   rø   r¨   Ústorage_keysr�   r<   rC  Úkindsr¯   r)  s
             r>   Úinventory_deleterN  "  sJ  € à€LÜ	�$Š˜4Ü�Š�t  XÔ.Ü�TÐ+¨CÓ4ˆÜ ¨Ó-ˆÞØ—I‘I˜d 2 A˜hÓ'Ä3ÀtÃ9ÈqÃ=Ñ+?ÐVXÑYˆEÜ Ð'HÈÈð  PRð  &Só  Tð  TØ26·,±,ØbØÐ" 3Ð'ô3)ó *ò 3)¨Q˜-Ô(ñ 3)ˆð *ð 	�‰Ð_ØÐ+¨CÐ0ô	2äˆd˜Ð4Ð5FÀsÈvÉ;ÔWØ�‰ÐOÐQTÐPYÔZ÷ 
ó ˆÜ	�)Ñ	˜CÑ	×'Ñ'°4Ð'Ó8ñ à�ˆ;Ðùò*÷ 
�ús   ŒBDÂDÂ"<DÄDÄ
Dc                 óx   • [        S0 U R                  S1S9DS[        R                  " U R                  5      0D6$ )Nr6  rÑ   Úvalue_centsrÓ   )r8   rÔ   r  r  r6  )rŠ   s    r>   Ú	lead_datarQ  8  s2   € ÜÑ[�'×$Ñ$¨g¨YÐ$Ð7Ñ[ÄAÇGÂGÈGÏMÉMÓDZÒ[Ð[r@   z/api/b/{bid}/leadsc           	      óª   • [        SS9 n[        R                  " X2U S5        [        USU [	        U5      U5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   r†  r'   )r   r   r  r  rQ  rÜ   s       r>   Úlead_createrS  <  s:   € ä	�$Š˜4Ü�Š�t  XÔ.Ü˜D ¨¬Y°wÓ-?ÀÓE÷ 
��úr  z/api/b/{bid}/leads/{rid}c           
      ó   • [        SS9 n[        R                  " XCU S5        [        USX5        [	        USX[        U5      5        [        XCU SSXR                  5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r'   zUpdated leadr   )r   r   r  r$   r#   rQ  r%   Ústager  s        r>   Úlead_updaterV  C  su   € ä	�$Š˜4Ü�Š�t  XÔ.´°t¸GÀCÔ0LÜˆt�G˜C¤I¨gÓ$6Ô7¼¸tÈÈ^Ð\cÐdg×huÑhuÔ9v÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðúr   z /api/b/{bid}/leads/{rid}/convertc                 óÒ  • [        SS9 n[        R                  " X2U S5        [        USX5      nUS   (       a  [        USUS   U 5      sS S S 5        $ [	        USU [        US   US   US	   US
   US   S9U5      nUS
   (       a  [	        USU [        US   SUS
   SS9U5        [        USX[        US   S95        [        X2U SSU5        S S S 5        U$ ! , (       d  f       W$ = f)NTrŒ   r†  r'   r‰  r1   r�   r‘   r’   r“   r°  )r�   r‘   r’   Úbilling_addressr°  r2   r�   zPrimary propertyrV   )r‰  r  r“   r°  )r‰  zLinked lead to new customer)r   r   r  r$   r  r8   r#   r%   )rØ   rø   r¨   r�   Úleadr|  s         r>   Úlead_convertrZ  K  s  € ä	�$Š˜4Ü�Š�t  XÔ.´V¸DÀÈÓ5Q°Ø�×Ü˜$˜{¨4°Ñ+>¸sÓC÷ 
‰ô ˜d ;¨s´4¸TÀ&¹\ÐPTÐU\ÑP]ÐdhÐipÑdqØ  ™O°$°w±-ñ4AØAEóGˆà�	�?Ü˜˜l¨3¬tÀÈÁÐUgÐptÐu~Ñpð  GIñ  0Jð  KOô  PÜˆt�G˜C¤D°X¸d±^Ñ$DÔEÜˆd˜Ð9¸'À#ÔF÷ 
ð €O÷ 
Œð €Oús   ŠA CÁA9CÃ
C&c           	      óÀ  • [        XUR                  UR                  5        UR                   H…  nUR                  (       a  [        U SUR                  U5        UR                  (       a  [        U SUR                  U5        UR                  (       d  Mh  UR                  (       d  M{  [        SS5      e    [        R                  " U5      n[        R                  " UR                  S5      5      US'   [        SUR                  UR                  UR                   UR"                  UR$                  S.UD6$ ! [         a  n[        S[        U5      5      eS nAff = f)	Nr)   r-   r_   zOAn estimate line can reference either a service or an inventory item, not both.ru  r$  )r‰  rŠ  rG   r°  r¨  rÓ   )rþ  r‰  rŠ  ru  Ú
service_idr$   r›  r
   r  Ú	calculatern   rY  rÃ   rÄ   r9   r8   rG   r°  r¨  )r�   rØ   rŠ   r¢  Útotalsr{   s         r>   Úestimate_datar_  Z  s  € Ü�4˜G×/Ñ/°×0CÑ0CÔDØ—”ˆØ�?�?Ü�4˜
 4§?¡?°3Ô7Ø××Ü�4Ð)¨$×*;Ñ*;¸CÔ@Ø�?�?‰?˜t×0×0Ñ0Ü Ð$uÓvÐvñ ð*Ü�{Š{˜7Ó#ˆô Ÿš F§J¡J¨wÓ$7Ó8€Fˆ<ÑÜð L˜G×/Ñ/¸G×<OÑ<OÐV]×VcÑVcØ—m‘m¨w×/AÑ/AñLØDJñLð Løô ó *Ü˜C¤ C£Ó)Ð)ûð*ús   Â8D9 Ä9
EÅEÅEz /api/b/{bid}/leads/{rid}/archivec                 óH  • [        SS9 n[        R                  " XCU S5        [        USX5      nUR	                  SUR
                  (       a  SOS[        5       X45        [        XCXR
                  (       d  SOS	SXS
   5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r'   zEUPDATE leads SET archived=?,updated_at=? WHERE id=? AND business_id=?r   r   zArchived leadzRestored leadr�   r   )r   r   r  r$   r€   r”   r!   r%   )rØ   rø   rŠ   r¨   r�   r<   s         r>   Úlead_archivera  l  sž   € ä	�$Š˜4Ü�Š�t  XÔ.´F¸4ÀÈÓ4P°Ø�‰Ð\Ðcj×cq×cqÑ^_ÐwxÔy|Óy~ð  @Cð  ^Hô  	IÜˆd˜·>·>™OÀÐW^Ð_bÐgmÑcnÔo÷ 
ð �ˆ;Ð÷	 
Œð �ˆ;Ðúr  c                 óN  • [        SS9 n[        R                  " X2U S5        [        USX5      nUR	                  SX45        [        X2U SSUUR                  S5      (       a  US    S	3OUS   5        UR	                  S
X45        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r'   zRDELETE FROM calendar_events WHERE business_id=? AND entity='leads' AND entity_id=?zDeleted leadr‰  r�   z (converted customer preserved)z.DELETE FROM leads WHERE id=? AND business_id=?r   )r   r   r  r$   r€   r%   rg   ©rØ   rø   r¨   r�   r<   s        r>   Úlead_deleterd  u  s±   € ä	�$Š˜4Ü�Š�t  XÔ.´F¸4ÀÈÓ4P°ð
 	�‰Ø`ØˆIô	
ô 	Ø�c˜.¨°Ø?B¿w¹wÀ}×?UÑ?Uˆs�6‰{ˆmÐ:Ñ;Ð[^Ð_eÑ[fô	
ð 	�‰ÐEÀsÀiÔP÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðús   ŠA=BÂ
B$z/api/b/{bid}/estimatesc                 óä   • [        SS9 n[        R                  " X2U S5        [        X0U5      n[	        USU [        S0 UD[        USU S5      SS.D6U5      sS S S 5        $ ! , (       d  f       g = f)	NTrŒ   r†  r*   ÚESTrŽ  )ry  r~   rÓ   )r   r   r  r_  r  r8   r&   r  s        r>   Úestimate_createrg  ‰  s`   € ä	�$Š˜4Ü�Š�t  XÔ.Ü˜4 GÓ,ˆÜ˜D ¨S´Ñ1w¸Ð1wÄ)ÈDÐQ\Ð]`ÐafÓBgÐovÓ1wÐx|Ó}÷ 
��ús   ŠAA!Á!
A/z/api/b/{bid}/estimates/{rid}c                 ó  • [        SS9 n[        R                  " XCU S5        [        USX5      nUS   S:w  a  [	        SS5      e[        USX[        X@U5      5        [        XCU S	SXS
   5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r*   r~   rŽ  rz   z=Sent estimates are locked. Create a revision to make changes.zUpdated estimatery  r   )r   r   r  r$   r
   r#   r_  r%   r  s         r>   Úestimate_updateri  ‘  s‘   € ä	�$Š˜4Ü�Š�t  XÔ.´F¸4ÀÈCÓ4T°Øˆx‰=˜'Ó!Ü Ð$cÓdÐdÜˆt�K ¬°d¸wÓ(GÔHÜˆd˜Ð.¨{¸3À6¹{ÔK÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðús   ŠA%A;Á;
Bc                 ó@  • [        SS9 n[        R                  " X2U S5        [        USX5      nUS   S:w  a  [	        SS5      eUR                  S	X45      R                  5       (       a  [	        SS
5      eUR                  SX45      R                  5       (       a  [	        SS5      eUR                  SX45      R                  5       (       a  [	        SS5      eUR                  SX45        [        X2U SSXS   5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r*   r~   rŽ  rz   zNOnly draft estimates can be permanently deleted. Void a sent estimate instead.zESELECT 1 FROM estimates WHERE business_id=? AND revision_of=? LIMIT 1z=This draft has a revision linked to it and cannot be deleted.z@SELECT 1 FROM jobs WHERE business_id=? AND estimate_id=? LIMIT 1z7This estimate is linked to a job and cannot be deleted.zDSELECT 1 FROM invoices WHERE business_id=? AND estimate_id=? LIMIT 1z<This estimate is linked to an invoice and cannot be deleted.z2DELETE FROM estimates WHERE business_id=? AND id=?zDeleted draft estimatery  r   ©r   r   r  r$   r
   r€   r‡   r%   ©rØ   rø   r¨   r�   r”  s        r>   Úestimate_deleterm  œ  s  € ä	�$Š˜4Ü�Š�t  XÔ.Ü�4˜ CÓ,ˆØˆx‰=˜'Ó!Ü Ð$tÓuÐuØ�<‰<Ð_ÐadÐ`iÓj×sÑs×uÑuÜ Ð$cÓdÐdØ�<‰<ÐZÐ\_Ð[dÓe×nÑn×pÑpÜ Ð$]Ó^Ð^Ø�<‰<Ð^Ð`cÐ_hÓi×rÑr×tÑtÜ Ð$bÓcÐcØ�‰ÐIÈ3È)ÔTÜˆd˜Ð4°[ÀÈÁ[ÔQ÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðús   ŠC6DÄ
Dz%/api/b/{bid}/estimates/{rid}/revisionc                 óv  • [        SS9 n[        R                  " X2U S5        [        USX5      nUS   S:X  a  [	        U5      sSSS5        $ UR                  SX45      R                  5       S	   nUR                  5        VVs0 s H  u  pgXg_M	     nnn[        US
[        [        5       XU[        R                  " U5      [        5       S95        [        USX[        SSSSSSSS95        [        X2U SSUSU SUS    35        [	        [        USX5      5      sSSS5        $ s  snnf ! , (       d  f       g= f)zéStart an in-place quote revision.

The estimate keeps the same id/code so one project never becomes multiple
operational estimates/jobs. A snapshot is stored for history, then the
current estimate is reopened as a draft for editing.
TrŒ   r†  r*   r~   rŽ  NzcSELECT COALESCE(MAX(revision_no),0)+1 FROM estimate_revisions WHERE business_id=? AND estimate_id=?r   r;  )r�   rì   rã  Úrevision_nor<  r–   rV   )r~   rÛ  rÜ  rÝ  Úpublic_token_hashÚpublic_token_issued_atr©  zStarted estimate revisionz	Revision z of ry  )r   r   r  r$   r*  r€   r‡   ru  r"   r8   r    rÃ   rÄ   r!   r#   r%   )	rØ   rø   r¨   r�   r  Úrev_noÚkr/  Úsnapshots	            r>   Úestimate_revisionru  ®  sy  € ô 
�$Š˜4Ü�Š�t  XÔ.Ü�4˜ CÓ,ˆØˆx‰=˜'Ó!Ü˜S“>÷	 
‰ð
 �|‰|ð  Bð  DGð  CLó  M÷  Vñ  Vó  Xð  YZñ  [ˆØ!$§¡¤Ô-¢™#˜!�!’#¡ˆÑ-ÜˆtÐ(¬´³À3ÐciÔx|÷  yCò  yCð  DLó  yMô  Y\ó  Y^ñ  *_ô  	`Üˆt�K ¬°GÈBÐ]_Ðlpð  DFð  ^bð  x|ñ  )}ô  	~Üˆd˜Ð7¸ÀCÈ)ÐTZÐS[Ð[_Ð`cÐdjÑ`kÐ_lÐHmÔnÜœ  [°Ó9Ó:÷ 
‰ùó .÷ 
�ús$   Š9D*Á7D*ÂD$ÂBD*Ä$D*Ä*
D8z#/api/b/{bid}/estimates/{rid}/statusc                 ó   • SS11 SkS.n[        SS9 n[        R                  " XSU S5        [        USX5      nUR                  UR                  US	   [        5       5      ;  a  [        S
S5      eS	UR                  0nUR                  S:X  a«  UR                  =(       d#    UR                  R                  5       (       a  SOS nU(       d  [        SS5      eUS   (       a6  US   [        R                  " 5       R                  5       :  a  [        SS5      eUR                  X‚R                  [        5       S9  [        USXU5        UR                  S:X  a:  [        USX5      n	[!        XPU5      n
U
(       a  [#        XPX“U
5      n
[%        XPX“U
5        ['        XSU SUR                  -   SXS   5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)Nr�  r/  >   r/  rª  rˆ  )rŽ  r�  TrŒ   r†  r*   r~   rz   z)This estimate cannot move to that status.rª  zVerbal agreementr_   z/Choose how the customer approved this estimate.r¨  z@This estimate has expired. Issue a new revision before approval.)rÛ  rÜ  rÝ  rä  ry  r   )r   r   r  r$   r~   rg   rò   r
   rÛ  rÜ  r    r   r´  rµ  r#   r!   ræ  rç  ré  r%   )rØ   rø   rŠ   r¨   Útransitionsr�   r  Úchangesre   ÚapprovedÚexisting_jobs              r>   Úestimate_statusr{  Ã  s“  € à  ˜Ò0NÑO€KÜ	�$Š˜4Ü�Š�t  XÔ.´F¸4ÀÈCÓ4T°Ø�>‰> §¡°°X±¼s»uÓ!EÓEÜ Ð$OÓPÐPØ˜'Ÿ.™.Ð)ˆØ�>‰>˜:Ó%Ø×*Ñ*×mÀW×EZÑEZ×E`ÑE`×EbÑEbÑ/AÐhlˆFÞÜ# CÐ(YÓZÐZØ�<×  S¨Ñ%6´t·z²z³|×7MÑ7MÓ7OÓ%OÜ# CÐ(jÓkÐkØ�N‰N¨6×@UÑ@UÔbeÓbgˆNÑhÜˆt�K ¨Ô0Ø�>‰>˜:Ó%Ü˜D ¨SÓ5ˆHÜ1°$¸3Ó?ˆLÞÜ3°D¸XÈ<ÓX�Ü" 4¨H¸,ÔGÜˆd˜˜K¨¯©Ñ6°{À3È6Á{ÔS÷' 
ð( �ˆ;Ð÷) 
Œð( �ˆ;Ðús   “FF<Æ<
Gc                 ó   • / nUn[        5       nU(       ah  XE;  ac  UR                  U5        U R                  SX45      R                  5       nU(       d   U$ UR	                  US   5        US   nU(       a  XE;  a  Mc  U$ )zBReturn this estimate and each ancestor revision, newest to oldest.zASELECT id,revision_of FROM estimates WHERE business_id=? AND id=?r�   Úrevision_of)rò   ró   r€   r‡   rà   )r�   rØ   rã  ÚidsÚcurrentrõ   r<   s          r>   Úestimate_lineage_idsr€  ß  s   € à
€CØ€GÜ	‹€DÞ
�gÓ)Ø�‰�ÔØ�L‰LÐ\Ð^aÐ]jÓk×tÑtÓvˆÞØð €Jð 	�
‰
�3�t‘9ÔØ�MÑ"ˆö �gÕ)ð €Jr@   c                 óˆ   • [        XU5      nU H0  nU R                  SXU45      R                  5       nU(       d  M.  Us  $    g )NzZSELECT * FROM jobs WHERE business_id=? AND (estimate_id=? OR latest_estimate_id=?) LIMIT 1)r€  r€   r‡   )r�   rØ   rã  r~  Úeidr<   s         r>   ræ  ræ  î  s\   € Ü˜T kÓ2€CÛˆØ�L‰LÐuÐwzð  @Cð  wDó  E÷  Nñ  Nó  Pˆßˆ3ØŠJñ ð r@   c                 ó²   • [        U SUS   U[        US   US   US   US   US   S95        [        U SUS   U5      n[        XUSSUS   US	    S
35        U$ )zÛMake the existing job's working scope match the latest approved quote.

Scheduling, status, assignee, checklist progress, and notes stay untouched.
Only customer/property/title plus the quoted item scope are refreshed.
r/   r�   r‰  rŠ  rG   r$  )r‰  rŠ  rG   Úlatest_estimate_idr$  zUpdated job scope from estimatery  z refreshed job items)r#   r8   r$   r%   )r�   rØ   r”  r¨   rî  Ú	refresheds         r>   rç  rç  ÷  s…   € ô ˆ4��s˜4‘y ¤TØ˜Ñ&Ø˜Ñ&Ø�'‰lØ˜t™9Ø�|Ñ$ñ&ô ô �T˜&  T¡¨3Ó/€IÜ	ˆ$�CÐ9¸&ÀÀTÁÈcÐRXÉkÈ]ÐZnÐKoÔpØÐr@   c                 óä   • U(       a*  U R                  SX45      R                  5       nU(       a  U$ [        XU5       H/  nU R                  SX45      R                  5       nU(       d  M-  Us  $    g )NzkSELECT * FROM invoices WHERE business_id=? AND job_id=? AND status!='void' ORDER BY created_at DESC LIMIT 1zpSELECT * FROM invoices WHERE business_id=? AND estimate_id=? AND status!='void' ORDER BY created_at DESC LIMIT 1)r€   r‡   r€  )r�   rØ   rã  Újob_idr<   r‚  s         r>   Úinvoice_for_estimate_lineagerˆ  	  s—   € ÞØ�L‰Lð  Gð  ILð  HTó  U÷  ^ñ  ^ó  `ˆÞØˆJÜ# D¨[Ö9ˆØ�L‰Lð  Lð  NQð  MVó  W÷  `ñ  `ó  bˆßˆ3ØŠJñ :ð r@   c                 óÎ   • [         R                  " U S   5       VVVs/ s H0  nUR                  5        VVs0 s H  u  p#US;  d  M  X#_M     snnPM2     snnn$ s  snnf s  snnnf )Nr$  )Úunit_cost_centsr  r\  r›  )rÃ   r'  ru  )r”  r¢  rs  r/  s       r>   Úinvoice_items_from_estimater‹    s[   € äŸ
š
 3 |Ñ#4Ô5õ7Ú5�ð !ŸJ™JœLÔršL‘S�Q¨AÐ5qÑ,q‹SˆQŠS™LÕrÙ5ó7ð 7ùÓrùô 7s   žA ·AÁAÁA ÁA c                 ó6  • U=(       d    [        XUS   5      n[        XUS   U(       a  US   OS5      nU(       d  gU R                  SXS   45      R                  5       S   nUS   U:  a  [	        SS5      e[        US   U(       a  US   OUS	   US
   [        R                  " [        U5      5      US   US   US   US   S9nUb  XXS'   [        U SUS   X5        [        XUSSUS   US    SUS   S-  S 35        [        [        U SUS   U5      5      $ )zSUpdate the one active final invoice for this estimate/revision chain, if it exists.r�   Nú{SELECT COALESCE(SUM(amount_cents),0) FROM transactions WHERE business_id=? AND invoice_id=? AND kind='payment' AND voided=0r   r0  rz   z‚This revision would make the final invoice lower than payments already recorded. Adjust the revision or correct the payment first.r‰  r‡  rG   Úsubtotal_centsÚdiscount_centsÚ	tax_cents)r‰  r‡  rG   r$  rŽ  r�  r�  r0  Údue_dater(   z#Updated final invoice from revisionry  z updated the final invoice to rn  z.2f)ræ  rˆ  r€   r‡   r
   r8   rÃ   rÄ   r‹  r#   r%   r*  r$   )	r�   rØ   r”  r¨   rî  r‘  r£  r2  rÍ  s	            r>   ré  ré    s¨  € à
×
=Ô)¨$°3°t±9Ó=€CÜ
& t°°D±	Æs¸#¸dº)ÐPTÓ
U€CÞØØ	�‰ð  Tð  VYð  ^bñ  Zcð  Udó  
e÷  
nñ  
nó  
pð  qrñ  
s€DØ
ˆ=Ñ˜DÓ Ü˜Cð  !eó  fð  	fÜØ˜Ñ&Þˆs�4Šy S¨¡]Ø�'‰lÜ—:’:Ô9¸#Ó>Ó?ØÐ+Ñ,ØÐ+Ñ,Ø�kÑ"Ø˜Ñ&ñ	€Fð ÑØ#ˆzÑÜ
ˆ4�
˜3˜t™9 SÔ0Ü	ˆ$�CÐ=¸jÈÈTÉÐVYÐZ`ÑVaÐUbð  cAð  BEð  FSñ  BTð  UXñ  BXð  Y\ð  A]ð  T^ô  _Ü”V˜D ¨C°©I°cÓ:Ó;Ð;r@   c                 ó   • [        XUS   5      nU(       a  [        [        XX#U5      5      $ [        XUS   5      nU(       a  US   OUS   nS Vs/ s H  n[	        USS9PM     nn[        U SU[	        US   US   XbS   US	   [        U SUS
5      SUS   [        R                  " U5      S9	U5      n	U(       a?  U R                  SSR                  S U 5       5      -   S-   U	S   [        5       U/UQ75      OS   U(       aG  SSR                  S U 5       5      -   S-   n
U R                  SU
-   S-   U	S   [        5       X/UQ75        U	$ s  snf )Nr�   éÿÿÿÿ)zConfirm access and measurementszPrepare materialszComplete installationzTest with customerzUpload completion photosF)rU  Údoner/   r‰  rŠ  rG   ÚJOBry  r$  )	r‰  rŠ  rã  r„  rG   ry  r~   r$  r%  zRUPDATE invoices SET job_id=?,updated_at=? WHERE business_id=? AND estimate_id IN (rM   c              3   ó&   #   • U  H  nS v •  M	     g7frü   rÓ   rþ   s     r>   r  Ú+create_job_from_estimate.<locals>.<genexpr><  s    é € ð  oEò  }DÐwxÕorò  }Dùr  Ú)z@SELECT id FROM invoices WHERE business_id=? AND estimate_id IN (c              3   ó&   #   • U  H  nS v •  M	     g7frü   rÓ   rþ   s     r>   r  r—  ?  s   é € ÐUkÒcjÐ^_ÕVYÒcjùr  zUUPDATE transactions SET job_id=?,updated_at=? WHERE business_id=? AND invoice_id IN ()ræ  r*  rç  r€  r8   r  r&   rÃ   rÄ   r€   r  r!   )r�   rØ   r”  r¨   r  ÚlineageÚroot_idÚtÚ	checklistrî  r¶  s              r>   rè  rè  3  s¿  € Ü  ¨#¨d©)Ó4€CÞ
ÜÔ/°¸À#ÓFÓGÐGÜ  ¨#¨d©)Ó4€GÞ"ˆG�BŠK¨¨D©	€Gñ 1pó  qò  1p¨1Œt˜ Ô&ñ  1p€Ið  qÜ�T˜& ¤T°c¸-Ñ6HÐUXÐYfÑUgÐt{ð  TXñ  PYØ�'‰l¤	¨$¨v°c¸%Ó @È
Ð^aÐbnÑ^oô  @D÷  @Jò  @Jð  KTó  @Uñ&Vð W[ó	\€Cö 5<ð 	‡L�LÐeÐfi×fnÑfnñ  oEñ  }Dó  oEó  gEñ  Eð  FIñ  IØ�d‘)œC›E #Ð/ wÑ/ô1ØAEøÞØ
LÈSÏXÉXÑUkÑcjÓUkÓMkÑ
kÐloÑ
oˆØ�‰ÐlÐmnÑnÐorÑrØ˜$‘i¤£ cÐ7¨wÑ7ô	9à€Jùò qs   ÁEz,/api/b/{bid}/estimates/{rid}/approve-and-jobc                 ó  • [        SS9 n[        R                  " XCU S5        [        USX5      n[	        X@U5      nUS   S;  a)  U(       a  O!US   S:X  a  [        SS	5      e[        SS
5      eUR                  S5      =(       d    SnU(       d-  US   S:w  a$  U(       a  U[        5       S S :  a  [        SS5      eUR                  =(       d    SR                  5       nUR                  =(       d    SR                  5       n	U(       d9  UR                  S5      =(       d    SR                  5       (       d  [        SS5      eSU=(       d    UR                  SS5      U	=(       d    UR                  SS5      UR                  S5      =(       d
    [        5       S.n
[        USXU
5        [        USX5      nU(       aq  [        X@XSU5      n[        U5      n[        X@XSU5      n[        XCU SSUSUR                  SUR                  SS5      5       S35        [        U5      X¼SS.sS S S 5        $  [!        X@XS5      n[        X@XSU5      n[        U5      X¼S S.sS S S 5        $ ! ["         a�  n[	        X@U5      nU(       av  [        X@XSU5      n[        U5      n[        X@XSU5      n[        XCU SSUSUR                  SUR                  SS5      5       S35        [        U5      X¼SS.s S nAsS S S 5        $ UeS nAff = f! , (       d  f       g = f)!NTrŒ   r†  r*   r~   )r�  rª  rŽ  rz   z/Mark this estimate as sent before approving it.z7This estimate cannot be approved in its current status.r¨  rV   rª  rx  zAThis estimate has expired. Create a revision before approving it.rÛ  r_   z8Choose an approval method before approving the estimate.rÜ  rÝ  )r~   rÛ  rÜ  rÝ  Úapprove_and_job_reusedr}  zReused existing job ry  r�   z while approving estimate.)r}  rî  r8  Úreused_existing_jobÚapprove_and_job_recoveredzRecovered existing job z$ after duplicate conversion attempt.F)r   r   r  r$   ræ  r
   rg   r!   rÛ  r    rÜ  r#   rç  r*  ré  r%   rè  rX  )rØ   rø   rŠ   r¨   r�   r”  rí  Úexpiresre   ÚnoterÍ  rî  r8  r{   s                 r>   Úapprove_and_jobr¤  E  sÛ  € ä	�$Š˜4Ü�Š�t  XÔ.Ü�4˜ CÓ,ˆô *¨$°3Ó7ˆàˆx‰=Ð 3Ó3Þð Ø�X‘ Ó'Ü# CÐ(YÓZÐZä# CÐ(aÓbÐbà—‘˜Ó%×+¨ˆÞ˜C ™M¨:Ó5¾'ÀgÔPSÓPUÐVYÐWYÐPZÓFZÜ Ð$gÓhÐhà×'Ñ'×-¨2×4Ñ4Ó6ˆØ×#Ñ#×) r×0Ñ0Ó2ˆÞ˜sŸw™wÐ'8Ó9×?¸R×FÑF×HÑHÜ Ð$^Ó_Ð_ð  Ø$×E¨¯©Ð0AÀ"Ó(EØ ×? C§G¡G¨O¸BÓ$?ØŸ'™' -Ó0×9´C³Eñ	
ˆô 	ˆt�K ¨Ô/Ü�4˜ CÓ,ˆæÜ+¨D°S¸hÓGˆHÜ˜(Ó#ˆCÜ& t°¸ÓBˆGÜ�$˜CÐ 8¸ÀCØ(¨¯©°¸¿¹ÀÀRÓ8HÓ)IÐ(JÐJdÐeôgä(¨›~°CÐ`dÑe÷U 
‰ðX	Ü(¨°#Ó;ˆCô # 4¨C°SÓ9ˆÜ$ S›>°Ð\aÑb÷y 
‰øô\ ó 	ô .¨d°sÓ;ˆHÞÜ/°¸À(ÓK�Ü˜hÓ'�Ü*¨4°C¸XÓF�Ü�d Ð$?À
È3Ø/°·±¸¸s¿w¹wÀtÈBÓ?OÓ0PÐ/QÐQuÐvôxä#,¨S£>¸ÐdhÑiÔi÷q 
‰ðr ˆIûð	ú÷] 
�úsC   ŠHK=È/I È;K=É 
K:É*A:K5Ë$K:Ë%K=Ë3K5Ë5K:Ë:K=Ë=
Lz /api/b/{bid}/estimates/{rid}/jobc                 óÖ   • [        SS9 n[        R                  " X2U S5        [        USX5      nUS   S:w  a  [	        SS5      e[        X0XB5      sS S S 5        $ ! , (       d  f       g = f)	NTrŒ   r†  r*   r~   rª  r_   z/Record customer approval before creating a job.)r   r   r  r$   r
   rè  rl  s        r>   Úestimate_jobr¦  …  sV   € ä	�$Š˜4Ü�Š�t  XÔ.´F¸4ÀÈCÓ4T°Øˆx‰=˜*Ó$Ü Ð$UÓVÐVÜ'¨°Ó:÷	 
��ús   ŠAAÁ
A(c                 ó¾  • [        XUR                  UR                  5        UR                  (       a<  U R	                  SXR                  45      R                  5       (       d  [        SS5      eUR                  S:X  a  UR                  (       d  [        SS5      eUR                  S:X  a  UR                  (       d  [        SS5      eUR                  (       a  UR                  (       d  [        SS5      eUR                  (       a7  UR                  (       a&  UR                  UR                  ::  a  [        SS5      eUR                  (       a7  UR                  (       a&  UR                  UR                  :  a  [        SS	5      eUR                  S
1S9n[        R                  " UR                   Vs/ s H  oDR                  5       PM     sn5      US'   U$ s  snf )Nr  r_   ú-Assign this job to a member of this business.Ú	scheduledz0Choose an installation date for a scheduled job.z(Choose a start time for a scheduled job.z/Choose a start time before setting an end time.ú*The end time must be after the start time.z4The due date cannot be before the installation date.r�  rÑ   r%  )rþ  r‰  rŠ  Úassigned_user_idr€   r‡   r
   r~   Úscheduled_dateÚscheduled_timeÚscheduled_end_timer‘  rÔ   rÃ   rÄ   r�  )r�   rØ   rŠ   r=   Úcs        r>   Újob_datar°  Ž  s‡  € Ü�4˜G×/Ñ/°×0CÑ0CÔDØ××¨¯©Ð5rÐtw÷  yQñ  yQð  tRó  )S÷  )\ñ  )\÷  )^ñ  )^Ü˜CÐ OÓPÐPØ‡~�~�{Ó"¨7×+A×+AÜ˜CÐ RÓSÐSØ‡~�~�{Ó"¨7×+A×+AÜ˜CÐ JÓKÐKØ×!×!¨'×*@×*@Ü˜CÐ QÓRÐRØ×× '×"<×"<À×A[ÑA[Ð_f×_uÑ_uÓAuÜ˜CÐ LÓMÐMØ××˜G×2×2°w×7GÑ7GÈ×H^ÑH^Ó7^Ü˜CÐ VÓWÐWØ	×	Ñ	 [ MÐ	Ð	2€DÜŸ:š:¸w×?PÒ?PÓ&QÒ?P¸!§|¡|¦~Ñ?PÑ&QÓR€DÐ	ÑØ€Kùò 'Rs   Æ5Gz/api/b/{bid}/jobsc                 óÞ   • [        SS9 n[        R                  " X2U S5        [        USU [	        S0 [        X0U5      DS[        USU S5      0D6U5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   r†  r/   ry  r•  rÓ   )r   r   r  r  r8   r°  r&   rÜ   s       r>   Ú
job_creater²  ¡  sZ   € ä	�$Š˜4Ü�Š�t  XÔ.Ü˜D ¨¬DÑ,t´8¸DÀWÓ3MÑ,tÔS\Ð]aÐbhÐilÐmrÓSsÒ,tÐuyÓz÷ 
��úó   ŠA
AÁ
A,z/api/b/{bid}/jobs/{rid}c                 óz  • [        SS9 n[        R                  " XCU S5        [        USX5      nUS   (       a2  UR                  US   :w  d  UR
                  US   :w  a  [        SS	5      e[        USX[        X@U5      5        [        XCU S
SXR                  5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r/   rã  r‰  rŠ  r_   z<A quoted job must retain its approved customer and property.zUpdated jobr   )r   r   r  r$   r‰  rŠ  r
   r#   r°  r%   r~   r  s         r>   Ú
job_updaterµ  ¨  s·   € ä	�$Š˜4Ü�Š�t  XÔ.´F¸4ÀÀsÓ4O°Øˆ}× 7×#6Ñ#6¸¸MÑ8JÓ#JÈg×NaÑNaÐcfÐgtÑcuÓNuÜ Ð$bÓcÐcÜˆt�F˜3¤8¨D°WÓ#=Ô>ÄÀdÐPSÐTaÐbhÐil×m{Ñm{Ô@|÷	 
ð
 �ˆ;Ð÷ 
Œð
 �ˆ;Ðús   ŠBB)Â)
B:z /api/b/{bid}/jobs/{rid}/schedulec                 ór  • [        SS9 n[        R                  " XCU S5        [        USX5      nUR                  (       d  [        SS5      eUR                  (       d  [        SS5      eUR                  (       a&  UR                  UR                  ::  a  [        SS5      eUR                  (       a<  UR                  S	XR                  45      R                  5       (       d  [        SS
5      e[        SUR                  UR                  UR                  UR                  UR                  S9nUR                  S5      (       d  UR                  US'   [        USXU5        [        XCU SSXR                   SUR                   35        [!        [        USX5      5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   r†  r/   r_   zChoose an installation date.zChoose a start time.rª  r  r¨  r©  )r~   r¬  r­  r®  r«  r°  r‘  zScheduled jobrl  )r   r   r  r$   r¬  r
   r­  r®  r«  r€   r‡   r8   r°  rg   r#   r%   r*  )rØ   rø   rŠ   r¨   r�   r  rÍ  s          r>   Újob_scheduler·  ²  s‚  € ä	�$Š˜4Ü�Š�t  XÔ.´F¸4ÀÀsÓ4O°Ø×%×%Ü Ð$BÓCÐCØ×%×%Ü Ð$:Ó;Ð;Ø×%×%¨'×*DÑ*DÈ×H^ÑH^Ó*^Ü Ð$PÓQÐQØ×#×#¨D¯L©LÐ9vÐx{÷  }Uñ  }Uð  xVó  -W÷  -`ñ  -`÷  -bñ  -bÜ Ð$SÓTÐTÜ˜;°g×6LÑ6LÐ\c×\rÑ\rØ'.×'AÑ'AÐSZ×SkÑSkÐry×rÑrñAˆà�w‰w�z×"Ñ"Ø&×5Ñ5ˆF�:ÑÜˆt�F˜3 6Ô*Üˆd˜˜O¨F°3×:PÑ:PÐ9QÐQRÐSZ×SiÑSiÐRjÐ7kÔlÜœ  V¨CÓ4Ó5÷! 
��ús   ŠFF(Æ(
F6z"/api/b/{bid}/jobs/{rid}/unschedulec                 ó¬  • [        SS9 n[        R                  " X2U S5        [        USX5      nUR	                  S5      =(       d    Sn[        SSSSS9nUR	                  S	5      (       a  UR	                  S	5      U:X  a  SUS	'   [        USXU5        [        X2U S
SXS   5        [        [        USX5      5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   r†  r/   r¬  rV   ry  )r~   r¬  r­  r®  r‘  zRemoved job from calendarry  )	r   r   r  r$   rg   r8   r#   r%   r*  )rØ   rø   r¨   r�   r  Úold_daterÍ  s          r>   Újob_unschedulerº  Ç  s´   € ä	�$Š˜4Ü�Š�t  XÔ.´F¸4ÀÀsÓ4O°Ø—‘Ð)Ó*×0¨bˆÜ˜:°RÀrÐ]_Ñ`ˆØ�7‰7�:×Ñ 3§7¡7¨:Ó#6¸Ó#@Ø!ˆF�:ÑÜˆt�F˜3 6Ô*Üˆd˜Ð7¸¸sÀvÁ;ÔOÜœ  V¨CÓ4Ó5÷ 
��ús   ŠB1CÃ
Cc                 óv  • [        SS9 n[        R                  " X2U S5        [        USX5      nUS   S;   a  [	        SS5      e/ S	QnU HC  u  pgnUR                  S
U SU S3X45      R                  5       (       d  M5  [	        SSU S35      e   UR                  SX45      R                  5       (       a  [	        SS5      eUR                  SX45      R                  5       (       a  [	        SS5      e[        X2U SSXS   5        UR                  SX45        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r/   r~   )Úin_progressÚ	completedrz   zfIn-progress and completed jobs are part of service history and cannot be deleted. Cancel them instead.))r(   r‡  r8  )r+   r‡  zpayment or expense)r3   r‡  Úwarranty)r.   Úrelated_job_idr9  )r,   r‡  r:  zSELECT 1 FROM z WHERE business_id=? AND z
=? LIMIT 1zThis job is linked to a z and cannot be deleted.zWSELECT 1 FROM attachments WHERE business_id=? AND entity='jobs' AND entity_id=? LIMIT 1zTThis job has uploaded files. Remove or retain the job so those records are not lost.zQSELECT 1 FROM tasks WHERE business_id=? AND entity='jobs' AND entity_id=? LIMIT 1z0This job has linked tasks and cannot be deleted.zDeleted jobry  z-DELETE FROM jobs WHERE id=? AND business_id=?r   rk  )	rØ   rø   r¨   r�   r<   Úblockersr6  rD  Ú
label_names	            r>   Ú
job_deleterÂ  Ô  sc  € ä	�$Š˜4Ü�Š�t  XÔ.´F¸4ÀÀsÓ4O°Øˆx‰=Ð7Ó7Ü ð  %Mó  Nð  Nò
ˆó (0Ñ#ˆE˜Ø�|‰|˜n¨U¨GÐ3LÈVÈHÐT^Ð_ÐadÐ`iÓj×sÑs×uÓuÜ# CÐ*BÀ:À,ÐNeÐ(fÓgÐgñ (0ð �<‰<ÐqÐsvÐr{Ó|÷  Fñ  F÷  Hñ  HÜ Ð$zÓ{Ð{Ø�<‰<ÐkÐmpÐluÓv×Ñ÷  Bñ  BÜ Ð$VÓWÐWÜˆd˜˜M¨&°¸±[ÔAØ�‰ÐDÀcÀYÔO÷' 
ð( �ˆ;Ð÷) 
Œð( �ˆ;Ðús   ŠA2D'Â BD'Ä'
D8z /api/b/{bid}/jobs/{rid}/progressc                 ó   • [        SS9 n[        R                  " XCU 5        [        USX5      nUR                  S:X  a,  US   (       a  UR                  S5      (       d  [        SS5      e[        USX[        UR                  UR                  [        R                  " UR                   Vs/ s H  ofR                  5       PM     sn5      S	95        [        XCU S
SXR                  5        S S S 5        SS0$ s  snf ! , (       d  f       SS0$ = f)NTrŒ   r/   r©  r¬  r­  r_   z8A manager must set the installation date and time first.)r~   r°  r%  zUpdated job progressr   )r   r   r  r$   r~   rg   r
   r#   r8   r°  rÃ   rÄ   r�  rÔ   r%   )rØ   rø   rŠ   r¨   r�   r  r¯  s          r>   Újob_progressrÄ  í  s  € ä	�$Š˜4Ü�Š�t Ô%¬6°$°v¸cÓ+F sØ�>‰>˜;Ó&°Ð4D×0EÈSÏWÉWÐUe×MfÑMfÜ Ð$^Ó_Ð_Üˆt�F˜3¤4¨w¯~©~ÀGÇMÁMÔae×akÒakð  FM÷  FWò  FWó  mXò  FWð  AB×myÑmyÖm{ñ  FWñ  mXó  bYñ  $Zô  	[Üˆd˜Ð2°6¸#¿n¹nÔM÷ 
ð �ˆ;Ðùò mX÷	 
Œð �ˆ;Ðús   ŠB"C<Â,C7Ã&C<Ã7C<Ã<
Dz/api/b/{bid}/invoicesc                 ó†  • [        SS9 n[        R                  " X2U S5        [        USUR                  U 5      nUS   S:w  a  [        SS5      e[        X0US	   5      n[        X0US	   U(       a  US	   OS 5      nU(       a2  [        X0XBXQR                  S
9nU=(       d    [        U5      sS S S 5        $ [        USU [        US   U(       a  US	   OS US	   [        USU S5      US   S[        R                  " [!        U5      5      US   US   US   US   UR                  S9U5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   r†  r*   r~   rª  r_   z5Invoices can only be created from approved estimates.r�   )r‘  r(   r‰  ÚINVrG   ÚopenrŽ  r�  r�  r0  )r‰  r‡  rã  ry  rG   r~   r$  rŽ  r�  r�  r0  r‘  )r   r   r  r$   rã  r
   ræ  rˆ  ré  r‘  r*  r  r8   r&   rÃ   rÄ   r‹  )rØ   rŠ   r¨   r�   r”  rî  rí  Úupdateds           r>   Úinvoice_createrÉ  ø  sD  € ä	�$Š˜4Ü�Š�t  XÔ.´F¸4ÀÈG×L_ÑL_Ð`cÓ4d°Øˆx‰=˜*Ó$Ü Ð$[Ó\Ð\Ü$ T¨c°$©iÓ8ˆÜ-¨d°s¸4±yÎcÀÀTÂÐW[Ó\ˆÞÜ& t°¸×FVÑFVÑWˆGØ×1œi¨Ó1÷ 
‰ô ˜D ¨C´ÀÀ]ÑASÖhkÐ[^Ð_cÒ[dÐquØ˜D™	¤y°°jÀÀUÓ'KÐRUÐV]ÑR^ÐflÜ—z’zÔ"=¸cÓ"BÓCÐSVÐWgÑShÐx{ð  }Mñ  yNØ˜+Ñ&°3°}Ñ3EÈw×O_ÑO_ñ1að bfóg÷ 
��ús   ŠB D2Â4A4D2Ä2
E z /api/b/{bid}/invoices/{rid}/voidc                 ój  • [        SS9 n[        R                  " XCU S5        [        USX5        UR	                  SX45      R                  5       S   nU(       a  [        SS5      e[        USX[        S	S
95        [        XCU SSXR                  5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r(   zhSELECT COALESCE(SUM(amount_cents),0) FROM transactions WHERE business_id=? AND invoice_id=? AND voided=0r   r_   zSA paid invoice cannot be voided. Refund processing is not included in this release.r/  )r~   zVoided invoicer   )r   r   r  r$   r€   r‡   r
   r#   r8   r%   Úreason)rØ   rø   rŠ   r¨   r�   r-  s         r>   Úinvoice_voidrÌ  	  sÌ   € ä	�$Š˜4Ü�Š�t  XÔ.´°t¸JÀsÔ0OØ�|‰|ð  Gð  ILð  HQó  R÷  [ñ  [ó  ]ð  ^_ñ  `ˆÞÜ Ð$yÓzÐzÜˆt�J˜s¤t°6Ñ':Ô;¼UÀ4ÈSÐQaÐblÐmp×qÑqô  >A÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðús   ŠBB!Â!
B2z/api/b/{bid}/transactionsc                 óÄ  • [        SS9 n[        R                  " X2U S5        UR                  S1S9nUR                  (       d  [        SS5      eUR                  [        R                  " 5       R                  5       :”  a  [        SS5      eUR                  (       a  [        US	UR                  U 5        UR                  S
:X  a«  UR                  (       d  [        SS5      e[        USUR                  U 5      nUR                  SXR                  45      R                  5       S   nUS   S:X  d*  [        R                  " UR                   5      US   U-
  :”  a  [        SS5      eUS   US'   OUR                  (       a  [        SS5      e[#        USU [%        S0 UDS[        R                  " UR                   5      0D6U5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   r†  r-  rÑ   r_   zEnter the transaction date.zIRecord payments and expenses when they occur, not as future transactions.r/   Úpaymentz#Select an invoice for this payment.r(   r�  r   r~   r/  r0  z;Payment exceeds the invoice balance or the invoice is void.r‡  z/Expenses attach to jobs, not customer invoices.r+   Úamount_centsrÓ   )r   r   r  rÔ   r   r
   r´  rµ  r‡  r$   Úkindr,  r€   r‡   r  r  r-  r  r8   )rØ   rŠ   r¨   r�   r=   r8  r2  s          r>   Útransaction_createrÑ    s§  € ä	�$Š˜4Ü�Š�t  XÔ.Ø×Ñ¨¨
ÐÐ3ˆØ�|�|Ü Ð$AÓBÐBØ�<‰<œŸ
š
›×.Ñ.Ó0Ó0Ü Ð$oÓpÐpØ�>�>Ü�4˜˜wŸ~™~¨cÔ2Ø�<‰<˜Ó"Ø×%×%Ü# CÐ(MÓNÐNÜ˜4 
¨7×+=Ñ+=¸cÓBˆGØ—‘ð  ^ð  `c÷  dvñ  dvð  _wó  x÷  Añ  Aó  Cð  DEñ  FˆDØ�xÑ  &Ó(¬A¯GªG°G·N±NÓ,CÀGÈMÑDZÐ[_ÑD_Ó,_Ü# CÐ(eÓfÐfØ" 8Ñ,ˆD�ŠNà×!×!Ü# CÐ(YÓZÐZÜ˜D °´DÑ4e¸4Ñ4eÌQÏWÊWÐU\×UcÑUcÓMdÒ4eÐfjÓk÷) 
��ús   ŠF=GÇ
Gz$/api/b/{bid}/transactions/{rid}/voidc                 ó>  • [        SS9 n[        R                  " XCU S5        [        USX5      nUS   (       a  [	        SS5      e[        USX[        SUR                  S	95        [        XCU S
SXR                  5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r+   Úvoidedrz   z#This entry has already been voided.r   )rÓ  Úvoid_reasonzVoided ledger entryr   )	r   r   r  r$   r
   r#   r8   rË  r%   r  s         r>   Útransaction_voidrÕ  -  s�   € ä	�$Š˜4Ü�Š�t  XÔ.´F¸4ÀÈsÓ4W°Øˆx�=Ü Ð$IÓJÐJÜˆt�N 3¬4°qÀWÇ^Á^Ñ+TÔUÜˆd˜Ð1°.ÀÇ^Á^ÔT÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðús   ŠA5BÂ
Bc                 ó
  • U(       d  g U R                  SU45      R                  5       nU(       a
  US   (       d  [        SS5      eUS   (       d3  U R                  SX45      R                  5       (       d  [        SS5      eg g )Nz,SELECT is_super,active FROM users WHERE id=?r”   r_   zAssigned user is not active.rŽ   r  z3Assigned user does not have access to this company.r  )r�   rØ   rí   r<   s       r>   Ú_validate_user_for_businessr×  8  s�   € Þ�FØ�‰ÐCÀWÀJÓO×XÑXÓZ€CÞ�c˜(—mÜ˜CÐ >Ó?Ð?Øˆz�? 4§<¡<Ð0mÐorÐn{Ó#|÷  $Fñ  $F÷  $Hñ  $HÜ˜CÐ UÓVÐVð $Hˆ?r@   z/api/b/{bid}/tasksc                 óÞ   • [        SS9 n[        R                  " X2U S5        [        X0UR                  5        [        USXR                  5       U5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   r†  r0   )r   r   r  r×  r«  r  rÔ   rÜ   s       r>   Útask_createrÙ  A  sM   € ä	�$Š˜4Ü�Š�t  XÔ.Ô0KÈDÐU\×UmÑUmÔ0nÜ˜D ¨×-?Ñ-?Ó-AÀ$ÓG÷ 
��úr³  z/api/b/{bid}/tasks/{rid}c           
      ó6  • [        SS9 n[        R                  " XCU S5        [        USX5        [	        X@UR
                  5        [        USXUR                  5       5        [        XCU SSXR                  5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r0   zUpdated taskr   )
r   r   r  r$   r×  r«  r#   rÔ   r%   rG   r  s        r>   Útask_updaterÛ  H  s—   € ä	�$Š˜4Ü�Š�t  XÔ.´°t¸GÀCÔ0LÔNiÐjnÐsz÷  tLñ  tLô  OMÜˆt�G˜C G×$6Ñ$6Ó$8Ô9¼5ÀÈ3È~Ð^eÐfi×jwÑjwÔ;x÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðús   ŠA1BÂ
Bc                 óê   • [        SS9 n[        R                  " X2U S5        [        USX5      n[	        X2U SSXS   5        UR                  SX45        S S S 5        SS0$ ! , (       d  f       SS0$ = f)	NTrŒ   r†  r0   zDeleted taskrG   z.DELETE FROM tasks WHERE id=? AND business_id=?r   ©r   r   r  r$   r%   r€   rc  s        r>   Útask_deleterÞ  P  s¥   € ä	�$Š˜4Ü�Š�t  XÔ.´F¸4ÀÈÓ4P°ÔRWÐX\ÐbeÐftÐu|ð  ~Að  FMñ  BNô  SOð  QU÷  Q]ñ  Q]ð  ^Nð  PSð  OXô  QY÷ 
à�ˆ;Ð÷ 
Œà�ˆ;Ðúó   ŠAA!Á!
A2r'   r1   r*   r,   r0   r/   )r'   r1   r*   r,   r0   r/   c                 ób  • UR                   (       d  [        SS5      eUR                  (       a7  UR                  (       a&  UR                  UR                  ::  a  [        SS5      e[	        XUR
                  5        UR                  =(       d    SR                  5       nUR                  =(       d    SR                  5       n[        U5      [        U5      :w  a  [        SS5      eU(       a4  [        R                  U5      nU(       d  [        SS5      e[        XXA5        UR                  5       $ )Nr_   zChoose a calendar date.rª  rV   zBChoose both a related record type and record, or leave both blank.z2That record type cannot be linked to the calendar.)Ú
event_dater
   Úend_timeÚ
start_timer×  r«  Úentityr    Ú	entity_idr;   ÚCALENDAR_LINK_TABLESrg   r$   rÔ   )r�   rØ   rŠ   rä  rå  r6  s         r>   Ú_calendar_event_datarç  \  sæ   € Ø××Ü˜CÐ 9Ó:Ð:Ø××˜G×.×.°7×3CÑ3CÀw×GYÑGYÓ3YÜ˜CÐ LÓMÐMÜ ¨×)AÑ)AÔBØ�N‰N× ˜b×'Ñ'Ó)€FØ× Ñ ×& B×-Ñ-Ó/€IÜˆFƒ|”t˜I“Ó&Ü˜CÐ dÓeÐeÞÜ"×&Ñ& vÓ.ˆÞÜ Ð$XÓYÐYÜˆt˜)Ô(Ø×ÑÓÐr@   z/api/b/{bid}/calendar-eventsc                 ó®   • [        SS9 n[        R                  " X2U S5        [        X0U5      n[	        USXU5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   r†  r4   )r   r   r  rç  r  r  s        r>   Úcalendar_event_createré  m  s@   € ä	�$Š˜4Ü�Š�t  XÔ.Ü! $¨7Ó3ˆÜ˜DÐ!2°3¸DÓA÷ 
��ús   Š2AÁ
Az"/api/b/{bid}/calendar-events/{rid}c           
      ó  • [        SS9 n[        R                  " XCU S5        [        USX5        [	        X@U5      n[        USXU5        [        XCU SSXR                  5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r4   zUpdated calendar itemr   )r   r   r  r$   rç  r#   r%   rG   )rØ   rø   rŠ   r¨   r�   r=   s         r>   Úcalendar_event_updaterë  t  sŠ   € ä	�$Š˜4Ü�Š�t  XÔ.´°tÐ<MÈcÔ0VÜ! $¨7Ó3ˆÜˆtÐ% c¨dÔ3´U¸4ÀSÐI`ÐarÐsv÷  xEñ  xEô  6F÷ 
ð �ˆ;Ð÷	 
Œð �ˆ;Ðús   ŠAA/Á/
B c                 óê   • [        SS9 n[        R                  " X2U S5        [        USX5      n[	        X2U SSXS   5        UR                  SX45        S S S 5        SS0$ ! , (       d  f       SS0$ = f)	NTrŒ   r†  r4   zDeleted calendar itemrG   z8DELETE FROM calendar_events WHERE id=? AND business_id=?r   rÝ  rc  s        r>   Úcalendar_event_deleterí  |  sx   € ä	�$Š˜4Ü�Š�t  XÔ.´F¸4Ð@QÐRUÓ4Z°Üˆd˜Ð3Ð4EÀcÈgÉ,ÔWØ�‰ÐOÐQTÐPYÔZ÷ 
ð �ˆ;Ð÷	 
Œð �ˆ;Ðúrß  z/api/b/{bid}/warrantiesc                 óF  • [        SS9 n[        R                  " X2U S5        [        X0UR                  UR
                  5        UR                  (       a  [        USUR                  U 5        [        USXR                  5       U5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   r†  r/   r3   )
r   r   r  rþ  r‰  rŠ  r‡  r$   r  rÔ   rÜ   s       r>   Úwarranty_createrï  …  sl   € ä	�$Š˜4Ü�Š�t  XÔ.´¸tÈ×H[ÑH[Ð\c×\oÑ\oÔ0pØ�>�>œ6 $ v¨g¯n©n¸SÔAÜ˜D ¨c×2DÑ2DÓ2FÀtÓL÷ 
��ús   ŠA>BÂ
B z/api/b/{bid}/warranties/{rid}c           
      óL  • [        SS9 n[        R                  " XCU S5        [        USX5        [	        X@UR
                  UR                  5        [        USXUR                  5       5        [        XCU SSXR                  5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r3   zUpdated warrantyr   )r   r   r  r$   rþ  r‰  rŠ  r#   rÔ   r%   Ú	item_namer  s        r>   Úwarranty_updaterò  �  s²   € ä	�$Š˜4Ü�Š�t  XÔ.´°t¸LÈÔ0QÔSaÐbfÐkr×k~Ñk~ð  @G÷  @Sñ  @Sô  TTÜˆt�L ¨×);Ñ);Ó)=Ô>ÄÀdÐPSÐTfÐgsÐtw÷  yJñ  yJô  AK÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðús   ŠA<BÂ
B#c                 óH  • / nSnU R                    H�  n[        R                  " UR                  5      n[	        UR
                  U-  5      oRU-  nUR                  [        UR                  UR                  [        UR
                  5      XES95        Mƒ     [        R                  " U R                  5      n[        U R                  U R                  U R                  U R                  U R                   U R"                  [$        R&                  " U5      X&X&-   U R(                  S9$ )Nr   )r›  rj  rk  rŠ  r0  )Úsupplierr~   Ú
order_dateÚexpected_arrivalÚreceived_dater¿  r$  rŽ  r�  r0  r°  )ru  r  r  Ú	unit_costrm   rk  rà   r8   r›  rj  rY  Útaxrô  r~   rõ  rö  r÷  r¿  rÃ   rÄ   r°  )rŠ   ru  Úsubtotalr¢  r  Úlinerù  s          r>   Ú_po_datarü  •  s?  € Ø
€E�qˆhØ—”ˆÜ�WŠW�T—^‘^Ó$ˆ¬3¨t¯}©}¸TÑ/AÓ+B dÈdÁNÀHØ�‰”T t×'8Ñ'8ÀT×EUÑEUÔ_bÐcg×cpÑcpÓ_qð  CGñ  Yö  	Zñ ô 	
�Š�—‘Ó€CÜ˜×)Ñ)°·±È7×K]ÑK]Ðov÷  pHñ  pHð  W^÷  Wlñ  Wlð  |C÷  |Rñ  |Rô  ^b÷  ^hò  ^hð  inó  ^oð  Gð  bjñ  bnð  u|÷  uBñ  uBñ  Cð  Cr@   z/api/b/{bid}/purchase-ordersc                 ó   • [        SS9 n[        R                  " X2U S5        UR                  (       a  [	        USUR                  U 5        [        U5      n[        USU S5      US'   [        USXU5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   r†  r/   r.   ÚPOry  )r   r   r  r¿  r$   rü  r&   r  r  s        r>   Ú	po_createrÿ  ž  so   € ä	�$Š˜4Ü�Š�t  XÔ.Ø×!×!¤6¨$¨v°g×6LÑ6LÈSÔ#QÜ�gÓˆ¬Y°tÐ<MÈcÐRVÓ-W  V¡Ü˜DÐ!2°3¸DÓA÷	 
��ús   ŠA+A?Á?
Bz"/api/b/{bid}/purchase-orders/{rid}c                 óö   • [        SS9 n[        R                  " XCU S5        [        USX5      n[	        U5      n[        USXU5        [        XCU SSXS   5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r.   zUpdated purchase orderry  r   )r   r   r  r$   rü  r#   r%   ©rØ   rø   rŠ   r¨   r�   r  r=   s          r>   Ú	po_updater  §  s�   € ä	�$Š˜4Ü�Š�t  XÔ.´F¸4Ð@QÐRUÓ4Z°ÔaiÐjqÓarÐ\`ÜˆtÐ% c¨dÔ3´U¸4ÀSÐIaÐbsÐtwð  }Cñ  yDô  6E÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðús   ŠAA'Á'
A8c                 óà  • / nSnU R                    HÏ  n[        R                  " UR                  5      n[        R                  " UR                  5      n[        UR                  U-  5      obU-  nUR                  [        UR                  UR                  UR                  [        UR                  5      UR                  XEU[        UR                  U-  5      S9	5        MÑ     [        U[        R                  " U R                  5      -  S-  5      n[        S0 SU R                  _SU R                   _SU R"                  _SU R$                  _SU R&                  _S	U R(                  _S
U R*                  _SU R,                  _SU R.                  _SU R0                  _SU R2                  _S[        U R4                  5      _S[6        R8                  " U5      _SU_SU_SX'-   _SU R:                  _6$ )Nr   )	r\  r›  rj  rk  rm  Úunit_price_centsrŠ  r0  r  rÂ   r‰  rŠ  r‡  Úwarranty_idrG   r~   ÚissueÚ	diagnosisr¬  r­  r«  Úbillabler$  rŽ  r�  r0  r°  rÓ   )ru  r  r  Ú
unit_pricerø  rm   rk  rà   r8   r\  r›  rj  rY  rm  Útax_percentr‰  rŠ  r‡  r  rG   r~   r  r  r¬  r­  r«  r  rÃ   rÄ   r°  )rŠ   ru  rú  r¢  r  r  rà  rù  s           r>   Ú_service_datar  ¯  sÈ  € Ø
€E�qˆhØ—”ˆÜ�gŠg�d—o‘oÓ&ˆ¬Q¯WªW°T·^±^Ó-D¨ÌCÐPT×P]ÑP]Ð^cÑPcÓLdÀeÐpuÑfuÐfnØ�‰”T T§_¡_À$×BSÑBSÐ`d×`pÑ`pÔz}ð  C÷  Lñ  Ló  {Mð  SW÷  S\ñ  S\ð  nsð  UZô  fið  jn÷  jwñ  jwð  x|ñ  j|ó  f}ñ  ~ö  	ñ ô 	ˆH”Q—W’W˜W×0Ñ0Ó1Ñ1°%Ñ7Ó8€CÜò  f˜G×/Ò/ð  f¸G×<OÒ<Oð  fÐW^×WeÒWeð  fÐry÷  sFò  sFð  fð  MT÷  MZò  MZð  fð  bi÷  bpò  bpð  fð  w~÷  wDò  wDð  fð  OV÷  O`ò  O`ð  fð  pw÷  pFò  pFð  fð  V]÷  Vlò  Vlð  fð  ~E÷  ~Vò  ~Vð  fô  `cð  dk÷  dtñ  dtô  `uð  fô  AE÷  AKò  AKð  LQô  ARð  fñ  bjð  fñ  uxð  fð  EMò  EQð  fð  X_÷  Xeò  Xeð  fð  fr@   z/api/b/{bid}/service-callsc           	      óP  • [        SS9 n[        R                  " X2U S5        [        X0UR                  UR
                  5        [        X0UR                  5        [        U5      oDR                  [        USU S5      S S9  [        USXU5      sS S S 5        $ ! , (       d  f       g = f)NTrŒ   r†  r,   ÚSVC)ry  r,  )r   r   r  rþ  r‰  rŠ  r×  r«  r  r#   r&   r  r  s        r>   Úservice_call_creater  ¸  sš   € ä	�$Š˜4Ü�Š�t  XÔ.´¸tÈ×H[ÑH[Ð\c×\oÑ\oÔ0pô  sNð  OSð  X_÷  Xpñ  Xpô  sqÜ˜7Ó#ˆ§[¡[´iÀÀ_ÐUXÐY^Ó6_Ðko [Ñ%pÜ˜D °¸$Ó?÷ 
��ús   ŠBBÂ
B%z /api/b/{bid}/service-calls/{rid}c                 ó"  • [        SS9 n[        R                  " XCU S5        [        USX5      n[	        X@UR
                  5        [        U5      n[        USXU5        [        XCU SSXS   5        S S S 5        SS0$ ! , (       d  f       SS0$ = f)NTrŒ   r†  r,   zUpdated service callry  r   )	r   r   r  r$   r×  r«  r  r#   r%   r  s          r>   Úservice_call_updater  À  s¶   € ä	�$Š˜4Ü�Š�t  XÔ.´F¸4ÀÐPSÓ4X°ÔZuÐvzð  @G÷  @Xñ  @Xô  [YÜ˜7Ó#ˆ¤V¨D°ÀÈÔ%NÔPUÐVZÐ`cÐdzð  |Kð  LOð  TZñ  P[ô  Q\÷ 
ð �ˆ;Ð÷ 
Œð �ˆ;Ðús   ŠA'A=Á=
Bz(/api/b/{bid}/service-calls/{rid}/invoicec                 óì  • [        SS9 n[        R                  " X2U S5        [        USX5      nUS   (       a#  [	        [        USUS   U 5      5      sS S S 5        $ US   (       d  [        SS	5      e[        5       n[        US
U S5      n[        S10 SU_SU _SUS   _SUS   _SU_SSUS   -   _SS_SUS   _SUS   _SS_SS_SUS   _SUS   _SS_SS_SS_SS US   -   _S!S"_S#S _S$S%_S&S"_S'[        5       _S([        5       _S)[        5       _6n[        US
U5        [        5       n[        S10 SU_SU _SUS   _S*US*   _S+U_S[        USU S,5      _SSUS   -   _SS-_SUS   _SUS   _SS_SUS   _SUS   _S.S"_S([        5       _S)[        5       _6n	[        USU	5        UR                  S/U[        5       X45        [        X2U S0SXS   5        S S S 5        [	        W	5      $ ! , (       d  f       N= f)2NTrŒ   r†  r,   r,  r(   r  r_   z>Mark this service call as billable before creating an invoice.r*   rf  r�   rì   r‰  rŠ  ry  rG   z
Service - r~   rª  r$  rŽ  r�  r   Útax_bpsr�  r0  r  Údeposit_bpsÚdeposit_centsr°  zGenerated from r¨  rV   r}  rÛ  zService invoicerÜ  rÝ  r–   r—   r‡  rã  rÆ  rÇ  r‘  zOUPDATE service_calls SET invoice_id=?,updated_at=? WHERE id=? AND business_id=?zCreated service invoicerÓ   )r   r   r  r$   r*  r
   r    r&   r8   r!   r"   r€   r%   )
rØ   rø   r¨   r�   ÚsvcÚest_idÚest_coder”  Úinv_idr£  s
             r>   Úservice_invoicer  È  s8  € ä	�$Š˜4Ü�Š�t  XÔ.´F¸4ÀÐPSÓ4X°Øˆ|×ÜœV D¨°C¸Ñ4EÀcÓJÓK÷ 
‰ð �:�Ü Ð$dÓeÐeÜ‹uˆœy¨¨k¸#¸eÓD�hÜò  V‘Fð  V¡sð  V°s¸=Ò7Ið  VÐVYÐZgÒVhð  VÑnvð  Vð  ~Jð  KNð  OVñ  KWò  ~Wð  Vñ  _ið  Vð  uxð  yEò  uFð  Vð  VYð  Zjò  Vkð  Vñ  {|ð  Vñ  EFð  Vð  QTð  U`ò  Qað  Vð  nqð  rò  n@ð  Vñ  LMð  Vñ  Z[ð  Vñ  jkð  Vð  rCð  DGð  HNñ  DOò  rOð  Vñ  []ð  Vñ  jnð  Vñ  Pð  Vñ  _að  Vô  nqô  nsð  Vô  Bô  Dð  Vô  PSô  PUð  VˆÜˆt�K Ô$Ü‹uˆœ$ò  T¡&ð  T±Sð  TÀSÈÒEWð  TÐ_bÐckÒ_lð  TÑyð  Tô  FOð  PTð  U_ð  `cð  diô  Fjð  Tð  q}ð  ~Að  BIñ  ~Jò  qJð  Tñ  RXð  Tð  dgð  htò  duð  Tð  EHð  IYò  EZð  Tñ  jkð  Tð  vyð  zEò  vFð  Tð  SVð  Wdò  Seð  Tñ  oqð  Tô  }@ô  }Bð  Tô  NQô  NSð  T�cÜˆt�J˜sÔ# T§\¡\ð  3Dð  FLô  MPó  MRð  SVð  E[ô  &\ô  ^cð  dhð  nqð  rKð  L[ð  \_ð  djñ  `kô  ^l÷ 
ô �S‹>Ð÷ 
�ús   ŠA	G%ÁE5G%Ç%
G3z/api/b/{bid}/duplicatesr�   r‘   r’   r“   c           
      ó  • [        5        n[        R                  " XaU 5        / nUR                  SU 45       GH/  n/ n	U(       a@  US   (       a6  UR	                  5       US   R	                  5       :X  a  U	R                  S5        U(       ar  US   (       ah  SR                  [        [        R                  U5      5      SR                  [        [        R                  US   5      5      :X  a  U	R                  S5        U(       a@  US   (       a6  UR	                  5       US   R	                  5       :X  a  U	R                  S5        U	(       d  GM  UR                  [        X‰S	95        GM2     US S
 sS S S 5        $ ! , (       d  f       g = f)NzMSELECT id,name,email,phone,billing_address FROM customers WHERE business_id=?r‘   z
same emailr’   rV   z
same phonerX  zsame address)Úreasonsé   )r   r   r  r€   rå  rà   r  ÚfilterrY  Úisdigitr8   )
rØ   r¨   r�   r‘   r’   r“   r�   Úoutr¯   Úscores
             r>   Ú
duplicatesr!  Ø  s-  € ä	Œ�Ü�Š�t Ô%ØˆØ—‘ÐmÐorÐnt×uˆAØˆEÞ˜˜7Ÿ¨¯©«°q¸±z×7GÑ7GÓ7IÓ(IÈ5Ï<É<ÐXdÔKeÞ˜˜7Ÿ¨¯©´´s·{±{À5Ó0IÓ(JÈBÏGÉGÔTZÔ[^×[fÑ[fÐghÐipÑgqÓTrÓLsÓ(sÐuz÷  vBñ  vBð  COô  vPÞ˜1Ð.×/°G·M±M³OÀQÐGXÑEY×E_ÑE_ÓEaÓ4aÐch×coÑcoÐp~Ôcß‰u�c—j‘j¤ aÑ!6×7ñ vð �3�Bˆx÷ 
��ús   ‹D?E:Å!E:Å:
Fc                 ó†   • U[         ;  a  [        SS5      e[        R                  " XX#S:X  a  SOS5        [	        XXB5        g )NrÚ   zAttachment target not found.r+   r†  Úread)ÚUPLOAD_ENTITIESr
   r   r  r$   )r�   r¨   rØ   rä  rø   s        r>   Úattachment_accessr%  æ  s9   € Ø”_Ó$Ü˜CÐ >Ó?Ð?Ü‡N‚N�4˜S°^Ó-C¡ÈÔPÜ
ˆ4�sÕr@   z!/api/b/{bid}/files/{entity}/{rid}rä  c                 óÌ   • [        5        n[        XCXU5        UR                  SXU45       Vs/ s H  n[        U5      PM     snsS S S 5        $ s  snf ! , (       d  f       g = f)Nz“SELECT id,filename,content_type,size,category,created_at FROM attachments WHERE business_id=? AND entity=? AND entity_id=? ORDER BY created_at DESC)r   r%  r€   r8   )rØ   rä  rø   r¨   r�   r¯   s         r>   rö  rö  í  ss   € ä	Œ�Ü˜$ C¨sÔ3Ø!%§¡ð  /Dð  FIð  QTð  EUô  "Vó  Wò  "V˜A”�Q–ñ  "Vñ  W÷ 
‰ùò W÷ 
�ús   ‹$A¯AÁAÁAÁ
A#.ÚOtherÚfileÚcategoryc              ƒ   óŠ  #   • [        5        n[        XcXU5        S S S 5        UR                  S5      I S h  v•N nUR                  5       I S h  v•N   [	        U5      S:”  a  [        SS5      eSnUR                  S5      (       a  SnO� [        R                  " [        R                  " U5      5       n	U	R                  U	R                  -  S:”  a  [        S	S
5      eSSSS.R                  U	R                  S5      nU	R                  5         S S S 5        U(       d  [        S	S5      e[)        5       n
[+        UR,                  =(       d    SR/                  SS5      5      R0                  S S nSR3                  S U 5       5      =(       d    Sn[4        S-  U
-  n [        SS9 n[        XcXU5        UR7                  U5        UR9                  S5        [;        [)        5       XX+U[	        U5      X¥S S =(       d    S[=        5       S9
n[?        USU5        [A        XcU SXU5        S S S 5        WS    US!.$ ! , (       d  f       GN"= f GN GNü! , (       d  f       GNF= f! [         ["        [$        [        R&                  4 a    [        S	S5      ef = f! , (       d  f       Nt= f! [B         a    URE                  SS9  e f = f7f)"Ni € i  € r^   zFiles must be 8 MB or smaller.rV   s   %PDF-zapplication/pdfi@x}r_   zImage dimensions are too large.z	image/pngz
image/jpegz
image/webp)ÚPNGÚJPEGÚWEBPz'Upload a valid PNG, JPEG, WebP, or PDF.z0Only PNG, JPEG, WebP, and PDF files are allowed.rŒ  Ú\rW   é    c              3   óX   #   • U  H   n[        U5      S :¼  d  M  US;  d  M  Uv •  M"     g7f)r…  z
N)Úord)rÿ   Úchs     r>   r  Úupload.<locals>.<genexpr>  s%   é € ÐO¢(˜B¬c°"«g°r©k“R¸bÈÑ>N—R‘R¢(ùs   ‚*™*¡	*rÈ  TrŒ   i€  éP   r'  )
r�   rì   rä  rå  ÚfilenamerÇ  rv   rÆ  r)  r–   rö  zUploaded attachmentrI  r�   )r�   r5  )#r   r%  r#  Úcloserô   r
   rp   r   rÇ  ÚioÚBytesIOÚwidthÚheightrg   Úformatr»   r   ÚOSErrorrn   ÚDecompressionBombErrorr    r   r5  rz  r�   r  r   Úwrite_bytesÚchmodr8   r!   r"   r%   ÚBaseExceptionrK  )rØ   rä  rø   r¨   r(  r)  r�   ÚcontentrÇ  Úimr)  r5  r  r=   s                 r>   ÚuploadrC  ô  s‡  é € ä	Œ�Ü˜$ C¨sÔ3÷ 
à—)‘)˜MÓ*×*€GØ
�*‰*‹,×ÐÜ
ˆ7ƒ|�KÓÜ˜CÐ @ÓAÐAØ€LØ×Ñ˜(×#Ñ#Ø&‰ð	OÜ—’œBŸJšJ wÓ/Ô0°BØ—8‘8˜BŸI™IÑ% hÓ.Ü'¨Ð,MÓNÐNØ$/°|È<ÑX×\Ñ\Ð]_×]fÑ]fÐgiÓj�Ø—	‘	”÷	 1ö Ü˜CÐ RÓSÐSÜ‹€CÜ�4—=‘=×0 L×9Ñ9¸$¸sÓCÓD×IÑIÈ$È3ÐO€HØ�W‰WÑO¡(ÓOÓO×_ÐS_€HÜ�IÑ˜cÑ!€Fð	Ü�dŠ^˜tÜ˜d¨°3Ô7Ø×Ñ˜wÔ'Ø�L‰L˜ÔÜœ›¨3ÀsÐjvÔ|ð  AHó  }Ið  VYð  loð  moð  cp÷  c{ð  t{ô  GJó  GLñ  MˆDÜ�4˜ dÔ+¬U°4¸SÐAVÐW]ÐbjÔ-k÷ ð �d‘ xÑ0Ð0÷E 
Žúâ*Ú÷ 1Ö0ûô
 '¤w¬z¼%×:VÑ:VÐWó 	OÜ Ð$MÓNÐNð	Oú÷ �^ûô ó Ø�‰ ˆÑ&Øðüs²   ‚K�H6›K·I¸KÁIÁ:KÂ*I  Â5AIÄI  ÄBKÆ	J% Æ&B JÈ&J% È.KÈ6
IÉ 	KÉKÉ
IÉI  ÉKÉI  É 1JÊKÊ
J"ÊJ% Ê!KÊ"J% Ê%K Ë Kz/api/b/{bid}/attachment/{rid}c           	      ó4  • [        5        n[        R                  " X2U 5        [        USX5      n[	        X2XS   US   5        S S S 5        [
        S-  WS   -  nUR                  5       (       d  [        SS5      e[        USUS	   S
9$ ! , (       d  f       NL= f)Nrö  rä  rå  rÈ  rÆ  rÚ   úAttachment file is unavailable.zapplication/octet-streamr5  )rÌ  r5  ©	r   r   r  r$   r%  r   rÎ  r
   r   ©rØ   rø   r¨   r�   r<   r  s         r>   ÚdownloadrH    s�   € ä	Œ�Ü�Š�t Ô%Ü�4˜ cÓ.ˆÜ˜$ C¨H©°c¸+Ñ6FÔG÷ 
ô �IÑ˜c -Ñ0Ñ0€FØ�>‰>×ÑÜ˜CÐ AÓBÐBä˜Ð*DÈcÐR\ÉoÑ^Ð^÷ 
�ús   ‹8B	Â	
Bz"/api/b/{bid}/attachment/{rid}/viewc           	      ó6  • [        5        n[        R                  " X2U 5        [        USX5      n[	        X2XS   US   5        S S S 5        [
        S-  WS   -  nUR                  5       (       d  [        SS5      e[        XTS   S	S
0S9$ ! , (       d  f       NM= f)Nrö  rä  rå  rÈ  rÆ  rÚ   rE  rÇ  rÊ  rÉ  rË  rF  rG  s         r>   Úview_attachmentrJ  (  s£   € ä	Œ�Ü�Š�t Ô%¬6°$°}ÀSÓ+M sÔO`ÐaeÐknÐs{Ño|ð  ~Að  BMñ  ~Nô  PO÷ 
ä�IÑ˜c -Ñ0Ñ0€FØ�>‰>×Ñ¤=°Ð5VÓ#WÐWÜ˜¨nÑ*=ÐG\Ð]eÐFfÑgÐg÷	 
�ús   ‹8B
Â

Bz/api/b/{bid}/exportc                 ó¬  • [        SS9 n[        R                  " X!U S5        [        [        5       Vs0 s H  o3[        X#U 5      _M     nn[        UR                  SU 45      R                  5       5      US'   [        5       US'   UR                  SU 45       Vs/ s H  n[        U5      PM     snUS'   UR                  S	U 45       VVs/ s H  nS
 Vs0 s H  ofXV   _M	     snPM     snnUS'   [        X!U SSU 5        S S S 5        [        [        R                  " WSS9SSSU S S  S30S9$ s  snf s  snf s  snf s  snnf ! , (       d  f       NJ= f)NTrŒ   rª   r:  rš   Úexported_atz'SELECT * FROM audit WHERE business_id=?ró  z-SELECT * FROM attachments WHERE business_id=?)r�   rä  rå  r5  rÇ  rv   r–   Úattachment_metadatazExported business recordsr˜   é   )Úindentzapplication/jsonrÊ  zattachment; filename="business-é   z-export.json"rË  )r   r   r  rø  r÷  r8  r8   r€   r‡   r!   r%   r   rÃ   rÄ   )rØ   r¨   r�   r6  r=   r¯   rs  s          r>   Úexport_businessrQ  1  s²  € ä	�$Š˜4Ü�Š�t  WÔ-Ü>DÄV¼nÓMºn°U”L ¨CÓ0Ò0¹nˆÐMÜ˜dŸl™lÐ+PÐRUÐQWÓX×aÑaÓcÓdˆˆZÑÜ›Eˆˆ]ÑØ+/¯<©<Ð8aÐcfÐbhÔ+iÓjÒ+i aœ$˜qž'Ñ+iÑjˆˆZÑð KO÷  KWñ  KWð  XGð  ILð  HNô  KOô  %Pò  KOð  FGÑ6ó  &AÒ6°¨©¢fÑ6ô  &Añ  KOò  %PˆÐ"Ñ#Üˆd˜Ð7¸ÀSÔI÷ 
ô ”D—J’J˜t¨1Ñ-Ð9KÐUjð  nMð  NQð  RTð  STð  NUð  MVð  Vcð  ldð  Ueñ  fð  fùò Nùò kùò &Aùó  %P÷ 
�úsM   Š*E´D0Á
AEÂD5Â0EÃ
D?ÃD:Ã$D?Ã+EÄ0
EÄ:D?Ä?EÅ
Ez/staticÚstatic)Ú	directory)r�   rW   c                  ó,   • [        [        S-  S-  5      $ )NrR  z
index.html)r   r   rÓ   r@   r>   ÚindexrU  A  s   € äœ˜X™ lÑ2Ó3Ð3r@   )rV   FrÓ  rB   )NN)rV   rV   rV   rV   )ár7  rÃ   rc  rh   rð   r>  Ú
contextlibr   Údatetimer   Úpathlibr   Útypingr   Úurllib.parser   Úfastapir   r	   r
   r   r   r   r   Úfastapi.responsesr   r   r   Úfastapi.staticfilesr   Ú starlette.middleware.trustedhostr   ÚPILr   r   Úemail_securityr   r   Ú	quote_pdfr   rV   r   r   r  r   r   r   r   r    r!   r"   r#   r$   r%   r&   r8   Úcurrent_userÚUserÚ	FINANCIALr÷  r$  r?   rD   ri   rC   Úadd_middlewarer]  Ú
middlewarerx   Úexception_handlerÚIntegrityErrorr|   rg   r‚   r‰   ÚpostÚSetupr¥   ÚLoginr§   r°   r¶   ÚPasswordChangerž   ÚputÚProfilerÉ   r˜   ÚBusinessCreater×   ÚpatchrY  ÚActiverÝ   rä   ÚAdminUserCreater÷   ÚAdminAssignmentsr	  r  r  r  ÚBusinessr  r  ÚMemberr  r  ÚRoler   Údeleter"  r*  r8  r;  rL  ÚEmailSettingsrP  Ú	EmailTestrZ  r`  rg  rƒ  Ú
QuoteEmailr˜  r¥  rÄ  rÐ  rÕ  rï  rü  rþ  r  r  ÚCustomerr  r	  ÚPropertyr  r  r  ÚServicer  r  r%  ÚInventoryItemr'  r)  r-  rF  rN  rQ  ÚLeadrS  rV  rZ  r_  ra  rd  r²  rg  ri  rm  ru  ÚEstimateStatusr{  r€  ræ  rç  rˆ  r‹  ré  rè  ÚEstimateApprovalr¤  r¦  r°  ÚJobr²  rµ  ÚJobScheduler·  rº  rÂ  ÚJobProgressrÄ  ÚInvoicerÉ  ÚVoidrÌ  ÚTransactionrÑ  rÕ  r×  ÚTaskrÙ  rÛ  rÞ  ræ  ÚCalendarEventrç  ré  rë  rí  ÚWarrantyrï  rò  ÚPurchaseOrderrü  rÿ  r  ÚServiceCallr  r  r  r  r!  r%  rö  rC  rH  rJ  rQ  ÚmountrU  rÓ   r@   r>   Ú<module>rŽ     sì  ðÛ 	Û Û Û 	Û Û Ý *Ý Ý Ý Ý !ç X× XÑ Xß FÑ FÝ +Ý Bß -ç 8Ý )ç ß b× b× bÑ bà�‘w˜t×0Ñ0Ó1Ð1Ñ2€Úz€	ò 
€Úv€òð ñ
ó ð
ñ
 Ð.¸È(Ø&(§i¢i°Ó&?À3Ó&F‘{ÈDÐ\`Ø13·²¸>Ó1JÈcÓ1QÑ-ÐW[ñ]€ð × Ñ Ð(¸¿	º	ÐBUÐVvÓ8w×8}Ñ8}ð  Bó  9CÐ ñ  Dð ‡��Óñó ðð6 ×Ñ�w×-Ñ-Ó.ñvó /ðvð ‡�ˆÓñó ðð ‡�Ð	ÓñAó ðAð ‡�Ð
Óð6˜QŸW™Wó 6ó ð6ð& ‡�Ð
ÓðJ�1—7‘7ð J Wð J¸ó Jó ðJð ‡�ˆÓðUˆTó Uó ðUð ‡�Ð
Óð�Gð  xð °tó ó ðð ‡�Ð
Óð	[�a×&Ñ&ð 	[¨dó 	[ó  ð	[ð ‡�ˆÓð˜AŸI™Ið ¨Tó ó ðð ‡�Ð	 Ó!ð=�Tó =ó "ð=ð ‡�Ð
!Ó"ð
0˜Q×-Ñ-ð 
0°Tó 
0ó #ð
0ð ‡�Ð/Ó0ð˜ð  q§x¡xð °tó ó 1ðð ‡�Ð	Óð
�dó 
ó ð
ð ‡�Ð
Óðc˜q×0Ñ0ð c¸ó có ðcð* ‡�Ð	-Ó.ð˜3ð ¨×);Ñ);ð À4ó ó /ðð0 ‡�Ð*Ó+ð
�Sð 
 1§8¡8ð 
°4ó 
ó ,ð
ð ‡�Ð
(Ó)ð	+�Cð 	+˜dó 	+ó *ð	+ð ‡�Ð	Óðt˜Dó tó  ðtð ‡�Ð	 Ó!ð�ð ˜QŸZ™Zð ¨Tó ó "ðð ‡�Ð	ÓðcˆSð c�dó có ðcð ‡�Ð
ÓðV�ð V˜QŸX™Xð V¨4ó Vó ðVòUð ‡�Ð$Ó%ð	�#ð 	˜#ð 	 a§f¡fð 	°$ó 	ó &ð	ð ‡�Ð%Ó&ð�Cð ˜Cð  Tó ó 'ðòò	òð ‡�Ð	&Ó'ð
˜3ð 
 Dó 
ó (ð
ð ‡�Ð	&Ó'ð˜3ð  q§¡ð ¸Dó ó (ðð" ‡�Ð
,Ó-ð˜Cð ¨¯©ð ¸ó ó .ðð˜Gð ¨ô ð6�sð 6˜sô 6òeð ‡�Ð
.Ó/ðS�sð S˜sð S¨1¯<©<ð SÀð SÈTó Só 0ðSò@#ôL#6ðL ‡�Ð	/Ó0ðP˜Sð P¨só Pó 1ðPð& ‡�Ð	¨€Ð6ð'�só 'ó 7ð'ð ‡�Ð
!°€Ð>ð-m Cð -m°ó -mó ?ð-mð` ‡�Ð	Óð�ð ˜$ó ó ðô.Zòôð ‡�Ð
"Ó#ðW˜ð W A§J¢Jð W°Dó Wó $ðWð ‡�Ð	'Ó(ð˜ð  ð ¨A¯JªJð ¸Dó ó )ðð ‡�Ð
#Ó$ðM˜ð M A§J¢Jð M°Dó Mó %ðMð ‡�Ð	(Ó)ð˜ð  ð ¨A¯JªJð ¸Dó ó *ðò\ð
 ‡�Ð
!Ó"ðL�sð L 1§9¢9ð L°$ó Ló #ðLð ‡�Ð	&Ó'ð�sð ˜sð ¨1¯9ª9ð ¸$ó ó (ðòfð ‡�Ð
"Ó#ðU˜ð U Q§_¢_ð U¸$ó Uó $ðUð ‡�Ð	'Ó(ð˜ð  ð ¨Q¯_ª_ð À$ó ó )ðòòð6 ‡�Ð*Ó+ð˜ð  ð ¨$ó ó ,ðò*\ð ‡�Ð
ÓðF�Cð F §¢ð F¨Dó Fó  ðFð ‡�Ð	#Ó$ð�Cð ˜Cð ¨¯ªð °Dó ó %ðð ‡�Ð
,Ó-ð�Sð ˜Sð  dó ó .ðòLð$ ‡�Ð-Ó.ð�Sð ˜Sð ¨¯©ð °tó ó /ðð ‡�Ð&Ó'ð�Cð ˜Cð  Tó ó (ðð& ‡�Ð
"Ó#ð~˜ð ~ A§J¢Jð ~°Dó ~ó $ð~ð ‡�Ð	'Ó(ð˜ð  ð ¨A¯JªJð ¸Dó ó )ðð ‡�Ð*Ó+ð˜ð  ð ¨ó ó ,ðð" ‡�Ð
1Ó2ð;˜#ð ; #ð ;¨4ó ;ó 3ð;ð( ‡�Ð0Ó1ð˜ð  ð ¨A×,<Ò,<ð À$ó ó 2ðò6òòô$	ò7ô
<ò2ð$ ‡�Ð
8Ó9ð=c˜ð =c ð =c¨A×,>Ò,>ð =cÀDó =có :ð=cð~ ‡�Ð
,Ó-ð;�Sð ;˜Sð ; dó ;ó .ð;òð& ‡�Ð
Óð{�3ð {˜qŸušuð {¨$ó {ó ð{ð ‡�Ð	"Ó#ð�3ð ˜3ð  q§u¢uð °$ó ó $ðð ‡�Ð-Ó.ð6�Sð 6˜Sð 6¨¯ªð 6¸Dó 6ó /ð6ð( ‡�Ð/Ó0ð	6�sð 	6˜sð 	6¨ó 	6ó 1ð	6ð ‡�Ð%Ó&ð�3ð ˜3ð  Dó ó 'ðð0 ‡�Ð-Ó.ð�Sð ˜Sð ¨¯ªð ¸Dó ó /ðð ‡�Ð
!Ó"ðg�sð g 1§9¢9ð g°$ó gó #ðgð  ‡�Ð
,Ó-ð�Sð ˜Sð ¨¯ªð °Tó ó .ðð ‡�Ð
%Ó&ðl˜3ð l q§}¢}ð l¸$ó ló 'ðlð0 ‡�Ð
0Ó1ð˜ð  ð ¨Q¯VªVð ¸ó ó 2ðòWð ‡�Ð
ÓðH�Cð H §¢ð H¨Dó Hó  ðHð ‡�Ð	#Ó$ð�Cð ˜Cð ¨¯ªð °Dó ó %ðð ‡�Ð&Ó'ð�Cð ˜Cð  Tó ó (ðð  ¸+Ø#¨W¸VñÐ ð
 ¨!¯/ª/ô  ð" ‡�Ð
(Ó)ðB˜cð B¨!¯/ª/ð B¸tó Bó *ðBð ‡�Ð	-Ó.ð˜cð  cð °!·/²/ð Àtó ó /ðð ‡�Ð0Ó1ð˜cð  cð ¨tó ó 2ðð ‡�Ð
#Ó$ðM˜ð M A§J¢Jð M°Dó Mó %ðMð ‡�Ð	(Ó)ð˜ð  ð ¨A¯JªJð ¸Dó ó *ððC�Q—_’_ô Cð ‡�Ð
(Ó)ðB�#ð B˜aŸošoð B°4ó Bó *ðBð ‡�Ð	-Ó.ð�#ð ˜#ð  a§o¢oð ¸4ó ó /ððf˜!Ÿ-š-ô fð ‡�Ð
&Ó'ð@˜Cð @¨¯ªð @¸4ó @ó (ð@ð ‡�Ð	+Ó,ð˜Cð  Cð °·²ð À4ó ó -ðð ‡�Ð
4Ó5ð˜ð  ð ¨ó ó 6ðð ‡�Ð	"Ó#ñ
�3ð 
˜Dð 
 cð 
°3ð 
Àð 
Èsô 
ó $ð
ò ð ‡�Ð	,Ó-ðW�Cð W˜sð W sð W°ó Wó .ðWð ‡�Ð
-Ó.ÙFJÈ3ÃiÐ]dñ #1�Sð #1 ð #1¨ð #1°ð #1¸:ð #1ÐY\ô #1ó /ð#1ðL ‡�Ð	(Ó)ð	_�ð 	_˜ð 	_ $ó 	_ó *ð	_ð ‡�Ð	-Ó.ðh˜ð h ð h¨ó hó /ðhð ‡�Ð	Óð	f˜ð 	f ó 	fó  ð	fð ‡	‚	ˆ)‘K¨$¨x©-Ñ8¸h€	Ñ Gð ‡�ˆƒñ4ó ñ4r@   