+
    |i|+                        ^ RI t ^ RIt^ RIt^ RIt^ RIt^ RIHtHt ^ RIH	t	 ^ RI
t
]P                  P                  R4      tR tR tR tR tR	 tRR
 ltRR ltRR ltRR ltRR ltR tRR ltR tR t]R8X  d
   ]! 4        R# R# )    N)date	timedeltarelativedeltaz~/.openclaw/secretsc                     \         P                  P                  \        V 4      p\        P
                  ! R R\        V4      P                  4       4      # )z\s+ )ospathjoinSECRETSresubopenread)filenamer
   s   & 5/Users/harvey/.openclaw/workspace/tools/quickbooks.pyread_secretr      s4    77<<*D66&"d4joo/00    c                 :   \        R4      p \        R4      p\        R4      p\        P                  ! RRRRRR	R
R	RRV  RV 2RRV 2.RRR7      p\        P                  ! VP
                  4      pRV9  d   \        RV 24      h\        \        P                  P                  \        R4      R4      P                  VR,          4       RV9   dE   \        \        P                  P                  \        R4      R4      P                  VR,          4       VR,          # )z-Get valid access token, refreshing if needed.zquickbooks-client-id.txtzquickbooks-client-secret.txtzquickbooks-refresh-token.txtcurl-sz-XPOSTz9https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer-Hz/Content-Type: application/x-www-form-urlencodedAccept: application/jsonz-u:z-dz'grant_type=refresh_token&refresh_token=Tcapture_outputtextaccess_tokenzToken refresh failed: zquickbooks-access-token.txtwrefresh_token)r   
subprocessrunjsonloadsstdout	Exceptionr   r	   r
   r   r   write)	client_idclient_secretr!   resulttokenss        r   get_access_tokenr-      s   67I >?M >?M^^dFC?(1]O,7G 'F ZZ&FV#09:: 	g<	=sCII&Q_J`a& RWW\\'#ABCHNNvVeOfg.!!r   c           	        \        4       p\        R4      pRP                  R VP                  4        4       4      pRV RV  RV R2p\        P
                  ! RR	VR
RV 2R
R.RRR7      p\        P                  ! VP                  4      # )z"Call a QuickBooks report endpoint.quickbooks-realm-id.txt&c              3   4   "   T F  w  rV R V 2x  K  	  R# 5i)=N ).0kvs   &  r   	<genexpr>qb_report.<locals>.<genexpr>:   s     ;NDAs!A3ZNs   -https://quickbooks.api.intuit.com/v3/company/z	/reports/?&minorversion=65r   r   r   Authorization: Bearer r   Tr   )	r-   r   r   itemsr"   r#   r$   r%   r&   )report_nameparamstokenrealm_idqueryurlr+   s   &&     r   	qb_reportrD   5   s    E45HHH;FLLN;;E9(9[MYZ[`Zaaq
rC^^c&ug.( 	'F ::fmm$$r   c           	         \        4       p\        R4      p^ RIpRV RVP                  P	                  V 4       R2p\
        P                  ! RRVRR	V 2RR
.RRR7      p\        P                  ! VP                  4      # )zRun a QuickBooks query.r/   Nr9   z/query?query=r;   r   r   r   r<   r   Tr   )
r-   r   urllib.parseparsequoter"   r#   r$   r%   r&   )sqlr@   rA   urllibrC   r+   s   &     r   qb_queryrK   E   s    E45H9(=QWQ]Q]QcQcdgQhPiiy
zC^^c&ug.( 	'F ::fmm$$r   c                 F     R \        V 4      R 2#    \        T 4      u # ; i)$z,.2f)floatstr)vals   &r   	fmt_moneyrQ   U   s+    5:d#$$3xs     c                     \         P                  ! 4       pV ^ 8X  d   VP                  ^R7      pTpM,VP                  ^R7      \        V ^,
          R7      ,
          pTpVP	                  R4      VP	                  R4      3# )r   )day)monthsz%Y-%m-%d)r   todayreplacer   strftime)months_backrU   startends   &   r   get_date_ranger[   [   sj    JJLEa!$1%[](KK>>*%s||J'???r   c                   . pV P                  R/ 4      P                  R. 4       F  pV'       d5   VP                  R4      V8w  d   \        W14      pVP                  V4       K?  VP                  R4      R8X  d   VP                  \        V4      4       Kq  VP                  R4      R8X  d0   VP                  R. 4      pV'       d   VP                  V4       K  K  RV9   g   K  VP                  \        V4      4       K  	  V# )z2Recursively extract rows from QB report structure.RowsRowgrouptypeSectionDataColData)getextract_rowsextendappend)datasection_namerowssectionr   colss   &&    r   re   re   e   s    D88FB'++E26GKK0L@w5CKK;;v)+KKW-.[[ F*;;y"-DD! wKKW-. 7 Kr   c           	        a \        V 4      w  r\        RV RV R24       \        RRVRVRR/4      pRV3R	 lloS! VP                  R
/ 4      4       R# )zProfit & Loss report.u   
📊 Profit & Loss:     → 
ProfitAndLoss
start_dateend_dateaccounting_methodAccrualc                 T  < V P                  R . 4       EF  pVP                  R4      R8X  Ed9   VP                  R/ 4      P                  R/ .4      ^ ,          P                  RR4      pV'       d;   \        RV,           R 24       \        RV,           VP                  4        24       S! VP                  R/ 4      V^,           4       VP                  R	/ 4      P                  R. 4      pV'       dp   \        V4      ^8  d]   \        RV,           RR
V^ ,          P                  RR4      ,           R R\	        V^,          P                  R^ 4      4       24       EKM  EKP  EKS  VP                  R4      R8X  g   EKl  VP                  R. 4      p\        V4      ^8  g   EK  V^ ,          P                  RR4      pV^,          P                  RR4      pV'       g   EK  \        T;'       g    ^ 4      ^ 8w  g   EK  \        RV,           RVR R\	        V4       24       EK  	  R# )r^   r`   ra   Headerrc   valuer     r]   SummaryzTotal 30 rb   035NuZ   ──────────────────────────────)rd   printupperlenrQ   rN   )		rows_dataindentrowheadersummaryrl   namerP   print_sections	   &&      r   r   cmd_pl.<locals>.print_section   s   ==+Cwwv)+2.229rdCAFJJ7TVWTF]OH:67TF]OFLLN+;<=cggfb16!8<'')R044YCs7|a/TF]O2hPR9S.STV-WWXYbcjklcmcqcqryz{c|Y}X~  A  07F*wwy"-t9>7;;w3Dq'++gs3CtchhQ1 4r$r!IcN;KLM! ,r   r]   Nr   r[   r~   rD   rd   )rT   rY   rZ   rh   r   s   &   @r   cmd_plr   x   sb    'JE	"5'se2
67_eCY' DN& $((62&'r   c                8   \        V 4      w  r\        RV RV R24       \        RRVRVRR/4      pVP                  R	/ 4      P                  R
. 4       EF  pVP                  R4      R8X  gh   VP                  R4      R8X  g   K2  VP                  R/ 4      P                  R/ .4      ^ ,          P                  RR4      P	                  4       R9   g   K  VP                  R	/ 4      P                  R
. 4       F  pVP                  R4      R8X  g   K  VP                  R. 4      p\        V4      ^8  g   K?  V^,          P                  R4      '       g   K_  \        RV^ ,          P                  RR4      R R\        V^,          P                  R^ 4      4       24       K  	  VP                  R/ 4      P                  R. 4      pV'       g   EK|  \        V4      ^8  g   EK  \        RRR R\        V^,          P                  R^ 4      4       24       EK  	  R# )zRevenue summary.u   
💰 Revenue: rn   ro   rp   rq   rr   rs   rt   r]   r^   r_   Incomer`   ra   rv   rc   rw   r   rb   rx   40r{   ry   
  zTOTAL REVENUEN)incomerevenue)r[   r~   rD   rd   lowerr   rQ   )rT   rY   rZ   rh   r   r   rl   r   s   &       r   cmd_revenuer      s   'JE	UG5R
01_eCY' D xx#''r2777x'CGGFOy,H778R $$Y5a8<<WbIOOQUjjwwvr*..ub9776?f,779b1D4yA~$q'++g*>*>47;;wr#:2">a	$q'++V]^_J`@a?bcd	 :
 ggi,00B?Gw3w<!+_R0)GAJNN7ST<U2V1WXY 3r   c           	       aa \        V 4      w  r\        RV RV R24       \        RRVRVRR/4      p. oVV3R	 loS! VP                  R
/ 4      4       SP	                  R RR7       ^ pSR,           F1  w  rVV^ 8  g   K  \        RVR R\        V4       24       WF,          pK3  	  \        RRR R\        V4       24       R# )zTop expenses breakdown.u   
💸 Expenses: rn   ro   rp   rq   rr   rs   rt   c                 4  <a V P                  R . 4       EFv  pVP                  R4      R8X  g   K  VP                  R/ 4      P                  R/ .4      ^ ,          P                  RR4      P                  4       o\        ;QJ d    V3R lR 4       F  '       g   K   RM	  R	M! V3R lR 4       4      '       d   VP                  R
/ 4      P                  R . 4       F  pVP                  R4      R8X  g   K  VP                  R. 4      p\        V4      ^8  g   K?   SP	                  V^ ,          P                  RR4      \        V^,          P                  R^ 4      ;'       g    ^ 4      34       K  	  S! VP                  R
/ 4      4       EKy  	  R#     K  ; i)r^   r`   ra   rv   rc   rw   r   c              3   ,   <"   T F	  qS9   x  K  	  R # 5iNr3   )r4   xr   s   & r   r7   9cmd_expenses.<locals>.collect_expenses.<locals>.<genexpr>   s     M,LqF{,Ls   TFr]   rb   N)expensecost	operating)rd   r   anyr   rg   rN   )r   r   r   rl   r   collect_expensesexpensess   &   @r   r   &cmd_expenses.<locals>.collect_expenses   s<   ==+Cwwv)+2.229rdCAFJJ7TVW]]_3M,LM333M,LMMM"wwvr266ubA776?f4#&779b#9D"4yA~!)$,OOT!W[[5LeTXYZT[T_T_`ghiTjToTonoNp4q$r  B !!45 ,!)$(s   AF#FFr]   c                     V ^,          #    r3   )r   s   &r   <lambda>cmd_expenses.<locals>.<lambda>   s    !r   T)keyreverse:N   Nrx   r   r{   r   TOTALN)r[   r~   rD   rd   sortrQ   )	rT   rY   rZ   rh   totalr   rP   r   r   s	   &      @@r   cmd_expensesr      s    'JE	eWE#b
12_eCY' D H6 TXXfb)*MMndM3Ec]]	7BtBiq3 012LE # 
DAi./
01r   c                 `   \        R4       \        R4      p V P                  R/ 4      P                  R. 4      pV'       g   \        R4       R# ^ pV F  pVP                  R/ 4      P                  RR	4      p\        VP                  R
^ 4      4      pVP                  RR4      pVP                  RR4      pW%,          pV\        P
                  ! 4       P                  4       8  d   RMRp\        RVR RVR R\        V4      R RV V 2	4       K  	  \        RRR R\        V4       24       R# )zOutstanding invoices.u   
🧾 Outstanding Invoices
zLSELECT * FROM Invoice WHERE Balance > '0' ORDER BY DueDate ASC MAXRESULTS 20QueryResponseInvoicez  No outstanding invoices.NCustomerRefr   UnknownBalanceDueDatezN/A	DocNumberr   u    ⚠️ OVERDUEz  #6r{   rz   12z Due: r   zTOTAL OUTSTANDINGr   )r~   rK   rd   rN   r   rU   	isoformatrQ   )	rh   invoicesr   invcustomerbalanceduedoc_numoverdues	            r   cmd_invoicesr      s   	
)*bcDxx,00B?H*+E77="-11&)D	1-.ggi'''+r*'*TZZ\-C-C-E'E#2GA;a}Ai.@-DF3%PWyYZ  
D$R()E*:);
<=r   c                   a \        V 4      w  r\        RV RV R24       \        RRVRV/4      pR
V3R lloS! VP                  R/ 4      4       R	# )zCash flow summary.u   
💵 Cash Flow: rn   ro   CashFlowrq   rr   c                   < V P                  R . 4       EF  pVP                  R4      R8X  d   VP                  R/ 4      P                  R/ .4      ^ ,          P                  RR4      pV'       d   \        RRV,           V 24       S! VP                  R	/ 4      V^,           4       VP                  R
/ 4      P                  R. 4      pV'       dR   \        V4      ^8  d?   \        RV,           RRR R\        V^,          P                  R^ 4      4       24       EK  EK  EK  VP                  R4      R8X  g   EK*  VP                  R. 4      p\        V4      ^8  g   EKO  \	        V^,          P                  R^ 4      ;'       g    ^ 4      ^ 8w  g   EK  \        RV,           RV^ ,          P                  RR4      R R\        V^,          P                  R^ 4      4       24       EK  	  R# )r^   r`   ra   rv   rc   rw   r   ro   rx   r]   ry   Totalr}   r{   rb   N)rd   r~   r   rQ   rN   )r   r   r   r   r   rl   print_cfs   &&    r   r   cmd_cashflow.<locals>.print_cf   s   ==+Cwwv)+2.229rdCAFJJ7TVWBtF{mF845,fQh7'')R044YCs7|a/T&[MGB<q71:>>RYZ[C\9]8^_`  07F*wwy"-t9>eDGKK,B,G,Ga&HA&MT&[MDGKK,CB+GqSWXYSZS^S^_fghSiIjHklm ,r   r]   Nr   r   )rT   rY   rZ   rh   r   s   &   @r   cmd_cashflowr      s]    'JE	ugU3%r
23ZeC" D
n TXXfb!"r   c                     \        R4       \        R4       \        R\        P                  ! 4       P                  4        24       \        R4       \	        ^4       \        ^4       \        4        R# )zFull financial snapshot.z  QUICKBOOKS FINANCIAL SNAPSHOTz  As of Nz2==================================================)r~   r   rU   r   r   r   r   r3   r   r   cmd_summaryr     sK    	(O	
+,	HTZZ\++-.
/0	(ONONr   c                  X    ^ RI Hp  \
        P                  R,          pV'       d
   V^ ,          MRp\        V4      ^8  d   \        V^,          4      M^pR	\        R
\        R\        R\        R\        R\        /pW$9  dC   \!        RRP#                  VP%                  4       4       R24       \
        P&                  ! ^4       VR9   d   WB,          ! V4       R# WB,          ! 4        R#   \         d5    \        P                  ! \
        P                  RRRRR.4       ^ RI Hp   EL i ; i)r   r   z-mpipinstallzpython-dateutilz-q:r   NNr   plr   r   r   cashflowzUsage: quickbooks.py [|z
] [months]N)r   r   r   r   )dateutil.relativedeltar   ImportErrorr"   r#   sys
executableargvr   intr   r   r   r   r   r   r~   r   keysexit)r   argscmdrT   cmdss        r   mainr     s    98
 88B<D$q'yC Y]Sa\F 	f;LLL;D &sxx		'<&=ZHI
77	&	1  9eY@QSWXY889s   C* *;D)(D)__main__r   r   r   )   )r   r$   r	   r   r"   datetimer   r   r   r   calendarr
   
expanduserr   r   r-   rD   rK   rQ   r[   re   r   r   r   r   r   r   r   __name__r3   r   r   <module>r      s      	 	  $ 0 
''

2
31"4% % @&(@Z0#2J>.#6< zF r   