DivyaShah2025 commited on
Commit
598282b
·
verified ·
1 Parent(s): 10e104c

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. Proforma_Invoice_Customer_20250713.pdf +85 -0
  2. app.py +53 -52
Proforma_Invoice_Customer_20250713.pdf ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ %PDF-1.3
2
+ %���� ReportLab Generated PDF document http://www.reportlab.com
3
+ 1 0 obj
4
+ <<
5
+ /F1 2 0 R /F2 4 0 R
6
+ >>
7
+ endobj
8
+ 2 0 obj
9
+ <<
10
+ /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
11
+ >>
12
+ endobj
13
+ 3 0 obj
14
+ <<
15
+ /BitsPerComponent 8 /ColorSpace /DeviceRGB /Filter [ /ASCII85Decode /FlateDecode ] /Height 241 /Length 12453 /Subtype /Image
16
+ /Type /XObject /Width 442
17
+ >>
18
+ stream
19
+ Gb"0WLKbJE_?TKo8mr*8NM=FajP")3]d0K30C@oY^p9UU9u2E*$?jB0$eC+C@kT7B4'Q<Dn$@CSAcX=gbZRtKiqn)Jj'rMAS14@'R@,ko1cGL:zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz!!!#sUU],teNbhP%0O<.cp8r<&";A[btT@>8o1d'Il<(7]e;J\#s27H-+B,9YgXHJ\:3(X.f@#d]Sg[AfOEH$\F;ab*;\aeji\;ma7XKk5;mT3L(1t,n"2B!2Z`Ue;`'?a2I9TI6#>4Drg`'$2"\BK82tEHneG(gZgbg<ba'uL$VE&0l%dN]'%$X&jGg%hOFm"=jU0<%*9`8IT:A&c<go3*s(10eq+/])'"0OKm@W^012W'(_C/s[CoQa:O^8jLq*!Hk.9_F)r5)%1DG>3-Y%Wh@?/*f*\o6Dn/q-003ANtT9[;f<k__a"S3)'m1,Pj-bMksKOHrIlo@co/^XMB8!D$(n,h<@6]e5g[i%nS,X0X+:$^Z0H?1!DaVr^uE0]f-fh[iMj<*qfsBfXfh/YN=a[+CQ[Q#p_3Eklp-Fg'!^]W>&pA8#YB9H'YL[L!!%j*oX&>"Hht'@INI\Gnb7.&9h1VTFuV,uZ$nW95J]DspP9bGfMaYETpu/Ibi?rNkljOHpt9Ua<D<^:C)^)asgl.L2@=YsF;Zk9X>-s/)O%"t*-r>e0+;O(#5oJ$tmTddlF?](WK^<RO6k3tJ/:WBDeUm[Q:.b@D8s?g5aGk\2Zh2;&UbhFMSFXe(m'<ir$0Z3kGAWU4G?F7OGqC.;BUXR=lSZYII:2(MaN/SeFi?G1C0\1qJ>6\a#YAXZR^B@cElCY$F/o'-r?'j94`:bS/Rd*KQ]UT%i-RnIbCXAMMg3Q#+rWd@i!kI$MTTPLir`JT#r4s5rQ+/_@H@()G4Z<F@k]lr5#[A9'YQ^0'LT8X\2iku/4gDtXgB!]B`O$k/o^%K`<,TO%Yb;.GP/Z!2NRf.8U%uUmc)RUkiWR=\FO>-q!I(_C?#7All](5as?Z"LbHTb-PFnEb;QS)P^kir7-a0"/9RTL5\`,t@X*gZ&;F.G^+Zu`,.bA`kekNj>gWo#_)$ITQuo(dZ?D;6C0))Q'LE?&[EPA5G,*qC+".IVR/8iRe;gZ\D-8K6OJFF(W,nt1?C)si#o7[#coWZ=W>OKDb"$$s3fVX)',g1@S#11SQ5EXCWiD>A,`R$N,DJoDhFoL/hQ8B=<TpE-Wrd<,W*SDVTe>o[!Q3'^66&%KW^,+Pl858Omr5Y`7r,qeg#\C0`9[!_H*8bnCPP+psaiY!uB\?P2\i?KT`,$`'#G<C0KpumhbH-4na^].a@R^L>*/6srRhV?e*Q7ZFKIdO_=s6lj+V;&3(eWJ&Tmd?=&BWX9s/\V:^O<<%g[I"bsP.C;9WM46`ig2p=]&DVc?M=t5125g=N5e?s%Auskgg3nT^YaP@G4RY'lc@'u>-MtkloB-!c6@`]S%NdpnkC&]rUfQ.P9!P$\\IQIh:jlJDPrD/]9j7pjFF[LR[0h3\h6LM/"[]bCV*fJm:r=u+fBY/6djD'k.f;N&(qfsoeLl"WRu7!f_s`!fPTkq@fqH%[`dDn0%Ril\L?1eN?^"ajg-1\=+gs:*3Ao%D+FPRbLo*NS")XOel]>3h7(>,B:`Ph'(V[W;a9$?n*n#j$T[kl&hJ%@#T"i@9D4jYnWrKiNU9>hq:%'pURR5J<jS(7om7J<GCF8Y4kI2n)-aYqE,_rDfXKEf^YA%2>n&1ZWk2];JY[L2?f,UZ6pgmWbO!h;\0'<`Y4!LfCHh_\HYA&*Eg_C6')euDqXW>;-<Dg0X&b^MIR>T!]<]]Sn>Pg3h2oOi"h[G%)HRIG-S5iN3]&B>Sc@Lhil]U^&r(W!e/*!QS3CMa/6.ioA8&>@>5saH,UN-^ht`H2adCt7roN"mHVR)oYB[Cq1jd59M@/iak^s*Ng+O]c%^00O>+.d_O:6CQ$+0,D$OKC_`U`NW_`bVUX]W%0P7]%]NbuO"P4r0Qp.>-uDtc%T;reSUe4GkG[mk6%WQ)qIYG\;MY/shHhn+R,eg3m\D:'Une*YoLk1X0"1iMmiO*KaZ>C0PRj>8X2PPP6D`P4I`CXFT,g7jG<VYXUp4PS,$lW1Q`A[W;,4D9,u**6<Y[t!@cHHpX1#A39=Q*;Fm:]<!fIo?&K/Xsju)/VM4XnUr-YgY!I1F&SYd&-KKWj?!Tea8m'<XocY.!`S1/usou<E8>hnpecIBlZ]\p+3FT.J1<a'sd.p_\>mVak$RVYm-RcNn\p6"04<T@oPc8KN3,$L[<TA(Of7.e:FDVq4@8mr@9lu<p?XAdW0SY#1Y1o\rIgd;Neme,1T&ZFK+VrYJ7eupMTkdFHVrcD+=7FCM6><Zj_[Uldi/&d8Yj:`I[umdo**mOTtFB'e@^Do(Tmch`67PBWgoG3csSq0k5_3jd-q<Y</<2NppF;nKVc%M2q^Y--kS4EBQH$F(nn+B<5F;c_5#Sno(2aO7U#/J,YPBmIjHo1S(hIY5=!$53p:41!)%Ko_/E^OQuSa-PqigoP#bMOB`OfE"slK]DF0,H50elaSk1A,hVM9>U19tkapJ1(2J-<:hNFS>Jq/:C[%_)r)"S:eWnIh?f1;'4cf6(R?7bhPe%nV2]Pr%c:QXXY;o!LRdtUQQ9p$nU<SfU3GB`sSTh)#AlUK!(hcL1QLUeA[&kWK:&T23K_+BP[oFQMdP;dP?b^c$l/K[X9c3gQ8<#,F@r?Yck`!S"p7L5kHhZr6bemki.r"iI,6#HID[IuB*Og44>[#4O1[L]'eV@(i,Lu5W=4#UP[LhonlY;k!SLI?HY5O@OpXG'1RT93TZaJf\VNO`g5"B4s>S%L%VkZsZdU6ip%V9VLKs]^eMDX)LD<0EZl\9@BVrOt/g>])XV4?E_d:k.DeRX5A_mQ>i*VK<)h\=o:]t%RM>,>5ro>`okOR-3Wrd7P`a>p-$=Qk$R0puru-@8q9?W,"bl,*`XpC*65/P1lpk`MVR-[\PBCQ#nX=g[tGP:'WHL[4D4c.5[h*at7G[@C=:4Lfk&DdDULWjLjEH)t9%rim'u[:si4/i54M/Vh)$YiCY7q/$XTSR:tbZZVN-%KPSMms]_jj`d]QZEJs=1::=U\/UHPg'Z,81S=Z-2D9\t&(;<nLhmrH]o^-W@mANl9hK9m,cVeCe9)o$bcR;9qaoKF\+uYdQ@'tn=]p2<s(.``Bh;Knj2R#irpL(f91_aub2k.p-%Di161QTg\siCMR=%]Pa1dOYZ0$=XOkDdMlELrC?Lu:23\5smPKQX.HmI+_;94-gYOukc\K9@[Da0H/E*^THJDk(_0'`2<<"$U&YZ7aXO7jIM6FNp`j@b0jQaLULjGr!,S%FUci-B=1.2XG&0HVu#UNI\0o!EjY-(BC-=tX!_^gQ)UE)2Y([+T9ek0/ftF,%'YPSca]MlB(!<pn]Ymb!:?lnn!Oc)"--G5o^hgYDJ[3q)E:B?S7QiV9%h8[i']dJ6a@h^;L#ipU3:.QMD7m.S853,$'1.)%9$JsrfJ+*P&.@%:r!`IAIf$h`G#d2Nd21P4b5D&_GNW[I\$l3Wn?qgBJuhaJhu)<?^Zm44rC8%MBKI7n[h"9$3N+7r/SHXKS&JOh7^S!L$@L6>d[4_!K*b]2[_N/]&u8L8#qS>g)pIP49UQ=nO89p`u$mS]2i*]8<JWVhoJMl'1:/=/nADfRIt43qC(Gj]PpA'G4XjpLpgkf[TB%55:6>jLL!W^f2L+@o^,]:WBm[%+@YeaH]d^5Yaj`E9H$m"sP.POlDFea(1-c'U#f?eaEq<C@g@.6Qm\1I[Ku[MUJ00EpM4Q5Ilk]kC3DT]o1o2FJadA)C.[%3JE(1o=q1bBMm4V^]]'C3^:AFpV?+`-SDUr<%B?hmJn)UM,9]PFnhT=J4P7:Qm_0r!etUks:c)aZ8X#d\`il<V"&:h`ZJgS:A)tA3+r.m.;`*W.KV#EmCc>d6<acP*Z.Dj]C0jPr&FtUV=I6j1%W*6COcUo=!"`7@I3LqXs/L8&TF66.J>cIs/A>]_>Fh/)NnED`[lmOnK*.d6taPo*s5@QlgcUr7sC4A$p0$qj(?2@>CF'S1(\^/,YdYqsmA6YL>]LkEfSeW/Pu'r&>k\'&(dPeuWfdK-I;9[d2+L#$TK=[,c?U;ea_/I.HI)?-iO7`s)+]%@,,8hARMjX]X62mU;=@]Rhmg-2NLQIP!Ro&[EM6cd"8!j2Gp?s%"k;iqR:rDkSodOu=mVTDhtl;cERIS32'ppg7LZ-4/.Ajg+!TQdXu,AaSeK2WqD\e*.@nd$.`n426gC]N;49%]?mc-B4=oK4'A'#55h^g)&*.]B7$OW6Or1\lq*fr26#hU3^-NNd8eCe.9MWX8&(92<TC_0!>2>\tY&BU:%o$4MOG[1p'\[#2*(R@4"1;pa9FFT*:TPmH`\Y?)MH53Y5N&,uZgb[H7/)A]UQ>1uF$#;J4>I%`r+n<_@MPPFPCV/)+MSeq'*nBfU\"geL!:+$Fr"g/!("VWGHN?V457=!2Wf>/l]B*<OAo1+c_C7!o*%T-J&`b4TQ%4!43o]>=f@i^:XkX4EPX>#%?_9NO-)lD>=g0QN-mo/ttn,OJhj5's[EiCSI!>F3XI#K1O$fec@hYO.t0a7JHRc<6[@_\qc=h<!gY'>8HJV'QFq:$uujBkbYR)>5ZM1_Qc`Fe?dR44$[KjL-#W>enT*>V!O40&K=:BQC'3rTta);OomIr(RJU=)PX?+3uH4+'?sgjb!="raBM4Y,4c2BWf9PLSkYEG4]JF3ge1S'haljV55G<>)$9/B%s4_='3II\`NJPO\h^gq=F<g-9m]o1K%/;e8dk.h5Oku^4TnWTId(VCaT/`>t5Ke1EHKmr!-K:o\8,L#mklQS9uVsqiI-#GP)k#AQi+e%G<h%7F.lC_3k#HdcY-OXJ&;1da.a@iS,FaT7aLP`V0s5=s>P^.P1ro`<*'kB1Q432&eS^Cq:^\<XW2k`@j``897!Vg$K+AmLE/P;<4XE^X-#W"a&'O/L1tuM#V_j0,>:Ol@!`Kr=Y8;/P5i31+cXlQF8D_bPk8[l!0lk0DCQ8^\^["V9$%e/XqqW/M1W7Vl'8$7;qStfFAFN*4IG?8f:>B<C3i@6`.l+ona$&Ci&cjnJC420M->mdm=,U7OE>4aR92E8[`h?aOXeD&>Wm\8P>\AjK#1W:GXWa-[SsC"d#$@b@F7=f7ThJKq,;]8)XTcqL703,!hjo]Y[2q&j-,RF7H'gNgK@.33MYq-q&:;b`]8sb<*BikPk@CpaOb#$CRaa(]FGQ4%RP\P;\H.//T2M5OWPZ6NP)r"l_%_Zp\(E>?V6<O&_&onX]3'6.F@HcecEL&U'S%LGi!=d(tKAonHeua`mDOiC&eO'5bsHC`REE=YJil]^?HqdE(P*?Nk/sEmiC5V*[eW*QY]iq`8[^T>#\2Y/Ec3"_lY[HtaIA>F99fW<.XmQ9+KLT:dQB;<rGPUNn!-O2bSB>1E/s&d`g8WYH5=kOXFTT"Z:4-,Ki<Wg+d#_"+$\$U8Lc@#dQW[[a]gVi`;XUW%W?hsKaFH0s>FBh1mZCI),^-XU%HJpr-e@]E3)X-D;?^)n^MTR0!DJQ/70nr[oVM*:E6HR)HgU+`?CfeR?T&O6&>kp=eHL($9Jc`VP!T;j+f>>C?CcWINFo'HOY[\1Nr$mCCW.EE2fJMPXdBpidO8^9fsfds/t+%[EhI,b1[H51b`geoXYW4F-,\\j$qcB#3A$L"*r[Ytg/AFf7Ils7L(>7s`q8h0OYa[b!(/urakOI#?2\`;RGV'TKS,hhXX],H_$,IEZA[&TNGU\XfDiei#>gj3ZBd]tN?g!J)2S!Nu!i^h8\dSb>:6hNG`(Z"GlV51*;o';?3l=f#ck?iH_SJH>2`LJ`#eLH[l[7U6l=$.e%HQ)iOBDm8g5H))Z.rgCmGGOt5?.ZIpijA(%hbsEGO(i=3/#Jk,6nR3$@Icl+%rLkqh@/*Te+)n[RZ5<S)qgYh]bWuc.X/cb0dfOW)anE9iq`0pP9s"n4D!4\;.\1/:Y2dURlk0l`Kul\%eCouLBZ`l/B]YQZ8>Em8K&Ytfa:+_IR($08O4"SWapbjgMcMU\tc+HOg55DG.m7%aa((EHq;63m,Efb:hD]U[-VGsae(u8Q-A-B/XjVsRbqb^VWIfR+fe3p3a?dp#6'%F^87[qd-XccQd+(.-U-TejY4,XV>JrbYg^NZ-Ni?pO\h_6nmU^-Mq9(D99M2N8fj$@3o@m69ZrYWWI4;=?gk,L]<cl4.D'fk0&0Ju^+ZFF<"E[\[]6aSP:6<obCos1RNk,%\b7tn6%F#e[S-GD_9%$=1c9"T`equ'd_n!`s$<TK:WG\E&lg#RQGdli;ff#X@0U4FOqh\hVcYThFkk\\^U]7U\+c?;r0Mn]Z4CY@Y))n)2&%h"J`G63P7[7_DiiGfY\1\NIXf`>]);W#K?GB0bJ"ghC"]r(gDAfj'@XDN4>t+6V)6F$M*WT^4St??DV/_`<%apa,a5SeH[(IHZZ)#.hB4jlUa.5'G'uXUP^M+PcRpA\[*GJua^@fcki`o^fQuqB<d6&nr"GA5Bdl0o2P9hKX[o4un^;OrOh,66``[&+ofTu'Z8+/LVihqG1ATD)0;[j=N.cT@e%_*mPk\#f8PAr??2T@V/sE_O*:5hKIas(<22THJn>KCCa-.d3-9ou2Gg$&q<h2<=9Z$9>9li'\.`P`d?qea3Q*+gMS17k.m61;O?YY9[NdK_'i=s^N8IYJ3gZsa0YHEStc$,QaOHpu0=uJl1B76%"=);'?KNtgQr4/4'#2!"!6SBpb9_%33L80\VCt!(ga,5/n,E.R)Fkkh@Y4T\6.8+o\fb\uh@AIJYZR':4j1&*_/"]PM@@<XO;^.6%BVHHHP`[V([VD)pEdkgn4S^cHC&(W(-/T*o-JdmM$@]'XcK?Gh;cM@[jG+1EX(-Vf]><AC4o$o075<,7pj'BL2]27l&31kr-<n@5%FiWj'hHMc%gj*_=uEJ[LCKNle2_63lon/C(nGYEPKf04BtmLdp;[+&8eO\<O$#!c'2c)Q*7Y]uD;g/PK6?%RcmgG--DUf\]r6m5^_>&cr1[48.P:J:YB_?a&\Ac<;YQA2h5qK-g%03?OO3W%a]GU@p/`S*ZR%<iT:<S[*5ZMdm(mffX/>&[%j7ZZZDZfGc;/`F.+daqSJs<MA.;Nh%5])V1s1c-rjfT5=/le:.8T)P8DT+TBR-N3re4B@F0YD$;"TM'N=8uih5tlTVPPcr-V>QB+-+BGpmKV=-]5YAM!u(2BH9$TONf.[<@S1P2&'-/bP:M/fht3!d]g5&\c!%QRET@o9p&,A13NE@AP3gmBe.Amd$4e+ju5D=k\e_.`h)9sQM>HQ82F&:OF*R)?k9=Qs&n:7Y)5()gY9(?NXL*5kHY^aNAWp:m=EfX5PEmcc%@f(q'U'*U\&oqZ^&]pPl8eE(D9cG,FcTB)"W:M/F7S-?IbIir3"p;9;<Ir[O$qdRf$F+$N;pSjQpH2ZqTJdjCap[5$/gg9Mp8R584HYWV"b;_thnJg50rgFX=WIl/K`Ti2;OercoX;P``X'f?;#^Z&).I2HOM&<Yfb&oVfTnQGQ</97lNr#iGU8f2BHdnt1;BDp]lZmTse`@h0D_P"UYg$i)O;RfN?6oDG?C<Wu^6Oi\\<e\Ne?KK1-pHCk-Sa/SX'+YAm</\Aamkk018:S$3G>$>65-F2tdh;4sm0HXOISu:aC@t::,:mT$r\X\.Q/cLC(Pd/d)aAm^0a3JUJU"_T.0>1Dt-C[&#*qP*+pF@8[C!"+;hVl1UfWK;Ofk\?"k.%s#9X3J'GMYIoBmnm]qspb9pK[ZW0D&g3H7_2::LhX=rjHFSlZmeO=#Mjpa$?_(B)OMEdbRUe!s`O#\C/f91+ce=QlIdDcr?-]r:%+0h;5Udr2TWfd3Oi;)dC11:KNE-+_bXNq`g,3&Pl@O*&O;eeW`Q]k&=*bQQt%GrJNLcHJ0a!1G**qW^5pSAJEhtr)S_(H6kf<F/X4F&bk1\[)rg*'T[G4l*ZTfBo=<Xrg&s!r0dh,nPm(p\_0kjSt)E%Su2BQs$0`tY-^$TbuF.g7nXB=`;/dU^XH78UqIVCqI*+,`-uc?gkX5+E'g,*Vo*%<9uk$3HFu-0cUgd[I80\gg?:'AiX/UDW@b+)O&%RB,A?3jQKh)?7fr+e$98%=X3/PW4$^Z;KPaE<-d2gIZlF1%RD:P5=t8ZURZDke3FN%ZiCCT&*T;9k3psm%[&S8\kEBLR6L$q(j1)Y#k;E7&d]tO$C@jY@K+7u^4GE'icB'R>2^J41&g8ihQA"YOXU2WpfVsrYlUn&NhUVA^#b"NeP*!*us2!#%iI,jVkLC_-UVG[[Zm:Tc^SZ>EM^<)JD[Fi)BpkK*l+D5%IkRQ-]o.76'J!aLMM34S]&u-Hbk:J(-?!EWSPm$eMS<OcaO8OIY@aSA6IXA3Xd&*:&(2))RETC0I<h*Oq-6i5rhfXfYM/-:LelCQUb2OM9K2WN,"3q:TD?/\dS$bnUh02M'9\[BRnb:/XT>4DdPAA^12)';/"bL]n[NoAoI6T%o@\OVLrQ4IMO^0;q]\ldd[bRmp:;lPAb=RfP*YM`V^^K-6<YQbP<1A,J;6:l9)eq?1AZgIFg&C>$AdYeE(I+,Hb.q1BGFE^n^QtrBkn5n)?'6HUug&]8B[&J/_@..9@\BeF'?kX9iV5"e+tFDq'FYl7Q$s/pp=Rh-MHsA:K5tTckL$aV)#nI81VUeF;%GRmS%S'qGd2%eUseRBh@U:f.6@rLXc`h*Z*35#pLr+;d'[EJ%rlXe)>7jh<Hk;V^4'ImQ?pFUo)8pe^Y[DAO_tk3.Crt=pc;"`MB?jYJ'i:Ner53,BO*])/c#?XH-%F^JWD)GL6%L-5Z7(=>N'_'h;HmG'q[dLV7X+@LC,3^Xq0g/cE7.l<K4Rj?&,u/-pA<6'RuBEspgQYmtLDj=E[il_LuCOssg8L](U@pBT62@eg=b<eP;C\Bg?L"DQOVWS"m3(jnQ)bhL.HZY%FW>!f-ogotrBD&opgZD-rW76Q+tG^O,#pg6f;16eTrrHn9bh*mo:f9h06KKnZDm8ZO(WoiH^%.m/\3l9))nPk[d-]g:8eSnd;MJ1J%%uT/)Ia+Y]$ZXa!BlZ_"rrS(TA["$D6^B&GIk8RO4JWjeTBUXSTlZIYILY\fg<B9:3.L6>aS]0q:Ae82GAc=/M8/<RV*rD-dr,2;WqVi7:X1$X.NE5I\UfIAln2g>)*MHip6*j>Fk:Ng\on#-q$lZ@P78<m7;peb/1N38V+Sgl`@kZTR'A_F\(B6`hn+70d[ujaXBQLCT(jUj*W:gae,)R.aK![S9T<O!KUrPX2p7WH'6P9^<Xht]SuVBgqm=ig:C.pY`N4kZ/bqNVn"/u_38o:,_qTFbH"Xn#F;e.`SY&fXN21$#%7=J#XuTJ9W)lS#HPa47fD8MArU9/8lOT)CSq^ho;Y'4p<j-H(>/hKf(`RTZkNJ0Ff?rZ8lRN`rSmM&in!!]X3r1H.5b+t^Oj&,+\(?':rU1t&98hL$-07;-QBK*4hT0-Cj\hM"IN[NCBh;K_UP=i*au\dK'QS:d"icK>9pS9Gc6EYY+2@ilm<86.F.sjE03bu_mi<>$(QoN5S.%o9<MGL`,j8_KIheMUK"<_5Q+Lt6aNn66D+dlpKNrV7'J_"R'PUJ1P]GKKagWjGcOYBZ\#J`G\+AKa'H8PIOio0%2-\*T>=S4jHVj,\SO2UfFJ@S4k?<VDVqDSkV4*o2EiDD!6gN!9Z)prZe:6d$ca/Od3bK)h,=tO`_m++(>,mr^8ElA)*62XhpO@&WOD^]2;1Tl>,DV>iI_GKFQ;OO>5:/'6e^9<4/Yg!ts%:d\ClUng%hA1?2VP<%=V=`Cgig(rjEffVb@o9k+)JnRZm2QGja;j<WoPp9?TsCV_,'cc<i?,^gJ@ag)f4V6cIH>_$h'QhbdIhb02b75"8-O#/.%/Ke$8@$?c##($1s@U-&JV$b5H<j<+==>d-L<SQ`oV.<CcL[Q4t,?!WeF1aa&:>BbL%m+Zk0a8u@:.M7d+9A7'-C7aP4+U/kn$RX2_u)\mR#p0h<"FCd8>hC'Mup"I9>9YBtNM(UOr;nCBo0;q>aSGbpf-7<*/!V2pjO,^T?q5sn5k662P*bk\',\]geNQ-tD2[jq7&fQaae>22=B^UJ0/OC,>>eKOX^>87UG_:`UrfBdN`-r%%HP](:nI3puSZp.LW*E*r<]dic/tF><6uK$3lsiNeN=ZfXdcl3Tllkd[YNiJ@dknp0$jhuo[\8J`V-f<pN`gp(39[lc8!>lhm)BPl:QRsrN60C1$E':,[%Db/Bh>7&Wc,W.S0jL-O[:(XEf!b].d9S:U,Fp!D>2>HT1tk/P=UD"9L+>*KnZ<48K!XFI+a[Zf<3\EPdB,"N,P*]bkRd'FB?\aIS.u7CW2"pf?79bM1gPhPaYAF(_Jn9X8'%IX`=?VGE,p$H/[G&]e!*#q8@C7h]^I\*pOCm2O\E2@1(#RaL,amIrhg^+j!Nu_cKs;.2Z\Xo6\R6j1Fn&kC<4BHXO68FIjb\/,bi^5fO>9W^pbp5-nTLkE:2;8$COVDTs(`#>ui,M`b!#C;bo9kKf&k9D-,R_pd$_<fnl"1M_0NdunZs4@]l'?G'\)kRjR86\ig;8\QHUmKuQj^D)?a63;?,Pc8pgb$"b560]eG6S`3CI=(^3<uJ,@]<f.tahgK=NZ4TCQV3$f1+b$Dr^LJb^UME`->WGQ<7703Cs(8P?Z5r8_F62?Fh6T1*aS"e&k%IZGp3Uq`5-n<GPjt3)ns<r1_2cRMX/(jMH-VC3=@Tgh+"]$`^:\.:X<6[J)R#Mk76ZnrK.O*Au3mC96;e/ToafQ.qRcHprU%2_mJ/_Z_%0Hp[1e#'D6OaK`[\IbLo,P]B1gCA(:oHhn_(7s&1:=5fW<>R&$9GbM)bg,!X'X@P:\bUJ08RA/b0c[9grECS+!=U'<+1!^?(29;WrkOI@t07VRPOD92kP_XqJO/PNdA[P+bP0@W;?Fi#Db),/FG_nMt-Rla"2U`7RaX'd'[X/p]d\ojLWqFZ/2HK1lpIe`Zp%?GgBJ`,[M)aA`Eh,bOSMHp.X%3)cofh.G*")YHpiAab?S+XfD.p&hII/7Jc7ZG]TVoe`O9U2HOP4+!2Bc3=K`_;jWFjYdE5CWXMSmAtc2SLKP\?i8aj8/FXpX5p5hgbN/h3D:N#P34hGO+_cXf2$n\)58XR.o^tku4IAr#@k?</*M@/s/Sd1KriP$-?2q:`oSVR)p!hcMZ[/U`'!a2rEb9fm<SkM8hJm\8\([kI4_K1*-1ZHlNbr2Ob1pp+qd,;P0>.lmlrC'_/lNq1lh4,6N7L=b21%2f/0`?8h<BB(P9e":[#Z^Ci65FPK#e54shBh\+c=chTiLaUm<;Ocr!eM@=Xa-rOnfq/"JGZ_#K`hQ)\*#$o;aniZs$<`_suUk\guaT@lnWS22&pabHrFC@ji=g[[(G=Vn%)%9Yji<3HNBDPnTfn4'31Y$rLh"0M\kI/B-J`[5.C@5t!DS*:+FSpJQjYC,p$siB;VBAg.4'^%fiKUh%J_tYA08^1D6V'-#[XY!S=NK'00VA0Eo<5iVebr$::0?'f^YeMQG\E#]lOAdO@"puklK.mUH*]"JEa<98S"DflI-'paj2[21<*(0?WZn::]&r(1PBBs15()j]d:h6X;"sj7Zl@Hmf6aW\_r59DC3nCVE/X2DH1&W@j+jXMWqKL_.n7[t!sai-4^*34p?Innp[6mdrp2X\@/Yr:=/>d-$$aPuS;>$8k<nm1$X0!@46MF7Ficj0fX%faa0iDt_+":8D^3kV@&4,FG3B%P,L;94>n+iEpM-4B&%_0N,2(t$HB'g,8\/pKl?=6T#K(G#OF?Lth=J@k7[Dr?`jXm<=$p$UIWF#kGgijF(c8o8HYoLGZ6JTGnqS3>JF8io?+p!oAbVruY(oO$=+Dsu&%`pSn3"JHH[FMnr#N)C,?2\b!9+a?4k8V+MXRB//jHJiFsLL7B)mGmLU>i1-iT106Y"u/JH"4e<u_4u!sf.f:X6Hk*d-'&GMFUQ?/K)(QtnnY'74]#[V2t8k"Rmc]#dl879X=I"9A3EiI#h-r=Ol?i_XDE$?*OQoig=DTZU^"Q^7r'K)bm8#gR3u$:!qT9C$o#e9phb:;]g.!;p=P,VAgeJE8Qhg*[!92#mUu9&+-[C44p*<Q]DFB(uuuTEbH^ntAiPM=@gW,W\,VY'@8F@SW#52:1(Y!:XFeBj!9TJ*gD_la`th2cf#;ptfR<fp_Tb.tsQ2(IJs\,l#45D?'Z-!g/;r(%F<&'TNmBM[Od!\WFo,SH/]jzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz#-\%33+J<Q~>endstream
20
+ endobj
21
+ 4 0 obj
22
+ <<
23
+ /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
24
+ >>
25
+ endobj
26
+ 5 0 obj
27
+ <<
28
+ /Contents 9 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
29
+ /Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /XObject <<
30
+ /FormXob.a883044e48600fc86784b4f6109cdee8 3 0 R
31
+ >>
32
+ >> /Rotate 0 /Trans <<
33
+
34
+ >>
35
+ /Type /Page
36
+ >>
37
+ endobj
38
+ 6 0 obj
39
+ <<
40
+ /PageMode /UseNone /Pages 8 0 R /Type /Catalog
41
+ >>
42
+ endobj
43
+ 7 0 obj
44
+ <<
45
+ /Author (anonymous) /CreationDate (D:20250713105430+05'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20250713105430+05'00') /Producer (ReportLab PDF Library - www.reportlab.com)
46
+ /Subject (unspecified) /Title (untitled) /Trapped /False
47
+ >>
48
+ endobj
49
+ 8 0 obj
50
+ <<
51
+ /Count 1 /Kids [ 5 0 R ] /Type /Pages
52
+ >>
53
+ endobj
54
+ 9 0 obj
55
+ <<
56
+ /Filter [ /ASCII85Decode /FlateDecode ] /Length 1132
57
+ >>
58
+ stream
59
+ GasaogN):5&:N^lqIQ[=\%"u7I"=4/+e)%))=XN3p.\p,]OP0I/7t3_hmP*<e0$NcV$*;Ph0mch-kLdQqL+n:N+2!F.8U6="t8q\LnETXDZ/;s]ri/.5`X1+ms;A06V&%4[X(lKo=reT/#(*0S4an,KP]4:&4V&d2ZY6'FWtB"'@ls5WnfA%61(Q,A_%<>:MiK`JWSjb3IH9Ybbt'Oo+2sTYOdP>h/<l)"7bj2TT5Ag7+Z.)IR>nl7@Ck)8e3N\N_mknfN@Sg]H1+Z]T0+-_m"kj3jdKdX:;Kmq^)ZVKmPC_;8TZlAJ3"4/DG'&TQ,0?JqXh/)f:J'8AkrFhKY$hU6*a<]-%+\h/L\$=N1lX!O-P88m[fl(kc8Khc%!%\WBM->EU#3qpMEJC,aS%Rb3BaZB[J)kC(&Wkm`#b&Nl%Lk(qCHFkmJfGRnI3N'o2"o4=P:(h*E38Ps'`ZOh0HnIDQt#0UL^p?QH;h<8T:q:+uYEimf]8QF`j+Zt=r.Zr=[N1WpRc+sF<:7qdQebc[Dc#os$S=lf#Hu^W(04D%.gH-V?B?k!u6j>l%J2TXV8`'+cB6qu:o]3IDetP\j=2S$A>2XN0O";=q?/cXi#[<P/&ElA[)BP#iQe[\lN3DsA+QSllKD$m9V26dPljtbT9\^1*]HrH3nNRR24;6fY<@jA([q.QSd\ol(;W\DD^4n;VX$kpjelQuTlW<iUYN>juc.KR6$tg60kOJ'+7OT$WLVii:Hhq^AGkS")UA+p+BgeDTJ/],M@BFctXUKPdR#)e1fWQ.F4]35umK!),&#0+Tc<Xm#5<4T34*3=9CFoq7=K*k<13o6>a&!5P!B`Q+*q?[0i!4-U4-/P/V`krso+J:X@BGD4XeoaqfIgj\m)m+X1P!+;N#!NELpboobUj(+p%0F<3@<8N`)T(D-P4CD;62("2j"lP!MpI]cDm90`rD5:$,gP#*>FG6#)P=(YgFp*9FF'&$#3E#Zq5BmPD6Ql;p2Se2,X=bCBAOgj%\GkN)S6gJ<u>XhsI>!_/.R5FumSNn:4M`(5Kc5S(\YO<6OoNCQ</0!b0\GTjT1h[(PT_N`F'^Z0/-NmCDtG6(M`IQI4Q*PtjtGI>NFkKMhS.(hijYT%A+%~>endstream
60
+ endobj
61
+ xref
62
+ 0 10
63
+ 0000000000 65535 f
64
+ 0000000073 00000 n
65
+ 0000000114 00000 n
66
+ 0000000221 00000 n
67
+ 0000012866 00000 n
68
+ 0000012978 00000 n
69
+ 0000013234 00000 n
70
+ 0000013302 00000 n
71
+ 0000013598 00000 n
72
+ 0000013657 00000 n
73
+ trailer
74
+ <<
75
+ /ID
76
+ [<0720c27523b68e3845d9276aee7c891f><0720c27523b68e3845d9276aee7c891f>]
77
+ % ReportLab generated PDF document -- digest (http://www.reportlab.com)
78
+
79
+ /Info 7 0 R
80
+ /Root 6 0 R
81
+ /Size 10
82
+ >>
83
+ startxref
84
+ 14880
85
+ %%EOF
app.py CHANGED
@@ -8,25 +8,24 @@ import json
8
  import base64
9
  from sendgrid import SendGridAPIClient
10
  from sendgrid.helpers.mail import Mail, Attachment, FileContent, FileName, FileType, Disposition
11
- import locale
12
- locale.setlocale(locale.LC_ALL, 'en_IN')
13
 
14
 
15
- # Initialize OpenAI
16
  client = OpenAI()
17
 
 
18
  def parse_natural_language(prompt_text):
19
- """Use OpenAI to parse plain text into structured product data"""
20
  system_prompt = (
21
  "You are a helpful assistant. Extract the customer name, address (if any), gstin (if any), "
22
- "and a list of products with description, code (optional), quantity, and price per unit, and GST percentage "
23
- "from the following text. Respond ONLY with a JSON object like this: "
 
24
  "{"
25
  "\"customer_name\": \"...\", "
26
  "\"gstin\": \"...\", "
27
  "\"address\": \"...\", "
28
- "\"products\": [{\"description\": \"...\", \"code\": \"...\", \"qty\": ..., \"rate\": ...}], "
29
- "\"gst_percentage\": 18"
30
  "}"
31
  )
32
 
@@ -43,7 +42,6 @@ def parse_natural_language(prompt_text):
43
  text = response.choices[0].message.content
44
  print("LLM returned:", text)
45
 
46
- # Try to parse
47
  try:
48
  parsed = json.loads(text)
49
  except json.JSONDecodeError:
@@ -51,47 +49,57 @@ def parse_natural_language(prompt_text):
51
 
52
  return parsed
53
 
 
54
  def format_inr(value):
55
- return "Rs. {:,.2f}".format(value)
 
 
 
56
 
57
  def generate_proforma_invoice_from_prompt(prompt_text):
58
- # Call OpenAI to parse
59
  parsed = parse_natural_language(prompt_text)
60
 
61
  customer_name = parsed.get("customer_name", "Unknown")
62
  address = parsed.get("address", "Not provided")
63
  gstin = parsed.get("gstin", "")
64
  products = parsed.get("products", [])
65
- gst_percentage = parsed.get("gst_percentage", 18)
66
 
67
  today = datetime.today().strftime("%Y%m%d")
68
  safe_customer_name = customer_name.replace(" ", "_")
69
  filename = f"Proforma_Invoice_{safe_customer_name}_{today}.pdf"
70
 
71
- subtotal = 0
72
  product_rows = []
 
73
  for row in products:
74
  try:
75
  description = str(row["description"])
76
  code = str(row.get("code", ""))
77
  qty = float(row["qty"])
78
  rate = float(row["rate"])
79
- row_total = qty * rate
80
- subtotal += row_total
 
 
 
 
 
 
81
  product_rows.append({
82
  'description': description,
83
  'code': code,
84
  'qty': qty,
85
  'rate': rate,
86
- 'total': row_total
 
 
 
87
  })
 
88
  except (KeyError, ValueError) as e:
89
  print(f"Error processing product row {row}: {e}")
90
  continue
91
 
92
- gst_amount = (subtotal * gst_percentage) / 100
93
- grand_total = subtotal + gst_amount
94
-
95
  c = canvas.Canvas(filename, pagesize=letter)
96
  width, height = letter
97
 
@@ -110,11 +118,10 @@ def generate_proforma_invoice_from_prompt(prompt_text):
110
  height=LOGO_HEIGHT,
111
  preserveAspectRatio=True
112
  )
113
- # Header
114
  c.setFont("Helvetica-Bold", 16)
115
  c.drawString(50, height - 70, "PROFORMA INVOICE")
116
 
117
- # Consignor Details
118
  c.setFont("Helvetica", 10)
119
  c.drawString(50, height - 100, "Consignor:")
120
  c.drawString(120, height - 100, "True Vybes")
@@ -123,11 +130,9 @@ def generate_proforma_invoice_from_prompt(prompt_text):
123
  c.drawString(50, height - 145, "Thane - 400607")
124
  c.drawString(50, height - 160, "GSTIN: 27BV0PS7767J2ZF")
125
 
126
- # Consignee Details
127
  c.drawString(50, height - 190, "Consignee:")
128
  c.drawString(120, height - 190, customer_name)
129
-
130
- # Multi-line address
131
  address_lines = address.split('\n')
132
  for i, line in enumerate(address_lines):
133
  c.drawString(120, height - 205 - (i * 15), line)
@@ -135,42 +140,41 @@ def generate_proforma_invoice_from_prompt(prompt_text):
135
  c.drawString(120, height - 205 - (len(address_lines) * 15), f"GSTIN: {gstin}")
136
  c.drawString(400, height - 190, f"Date: {datetime.today().strftime('%d-%b-%Y')}")
137
 
138
- # Product Table
139
  table_top = height - 250 - (len(address_lines) * 15)
140
  c.line(50, table_top, width - 50, table_top)
141
- c.setFont("Helvetica-Bold", 10)
142
  c.drawString(50, table_top - 15, "Description")
143
- c.drawString(250, table_top - 15, "Code")
144
- c.drawString(350, table_top - 15, "Qty")
145
- c.drawString(400, table_top - 15, "Rate/Unit")
146
- c.drawString(480, table_top - 15, "Value")
 
 
147
  c.line(50, table_top - 20, width - 50, table_top - 20)
148
 
149
-
150
  # Product rows
151
- c.setFont("Helvetica", 10)
152
  row_y = table_top - 35
 
153
  for product in product_rows:
154
  c.drawString(50, row_y, product['description'])
155
- c.drawString(250, row_y, product['code'])
156
- c.drawString(350, row_y, str(product['qty']))
157
- c.drawString(400, row_y, format_inr(product['rate']))
158
- c.drawString(480, row_y, format_inr(product['total']))
 
 
159
  row_y -= 20
160
 
161
- # Totals
162
  c.line(350, row_y - 10, width - 50, row_y - 10)
163
- c.drawString(400, row_y - 25, "Total")
164
- c.drawString(480, row_y - 25, format_inr(subtotal))
165
- c.drawString(400, row_y - 40, f"GST @ {gst_percentage}%")
166
- c.drawString(480, row_y - 40, format_inr(gst_amount))
167
- c.line(350, row_y - 45, width - 50, row_y - 45)
168
  c.setFont("Helvetica-Bold", 10)
169
- c.drawString(400, row_y - 60, "GRAND TOTAL")
170
- c.drawString(480, row_y - 60, format_inr(grand_total))
171
 
172
- # Bank Details
173
- bank_y = row_y - 90
174
  c.setFont("Helvetica", 10)
175
  c.drawString(50, bank_y, "Bank Details For True Vybes")
176
  c.drawString(50, bank_y - 15, "A/C Name: True Vybes")
@@ -178,7 +182,6 @@ def generate_proforma_invoice_from_prompt(prompt_text):
178
  c.drawString(50, bank_y - 45, "A/C Type: Current")
179
  c.drawString(50, bank_y - 60, "IFSC Code: KKBK0000656")
180
 
181
- # Terms
182
  terms_y = bank_y - 90
183
  c.drawString(50, terms_y, "Other terms and conditions:")
184
  c.drawString(50, terms_y - 15, "GST: Extra at actuals")
@@ -187,18 +190,15 @@ def generate_proforma_invoice_from_prompt(prompt_text):
187
  c.drawString(50, terms_y - 60, "Validity: The validity of this price is 10 days from today")
188
  c.drawString(50, terms_y - 75, "Freight: Extra on To-Pay basis")
189
 
190
- # Signature
191
  c.line(width - 150, terms_y - 90, width - 50, terms_y - 90)
192
  c.drawString(width - 140, terms_y - 105, "Authorised Signatory")
193
 
194
  c.save()
195
 
196
- # ✅ Send the email
197
  send_invoice_email(filename)
198
 
199
  return filename
200
 
201
-
202
 
203
  def send_invoice_email(pdf_filename):
204
  SENDGRID_API_KEY = os.getenv("SENDGRID_API_KEY")
@@ -206,7 +206,7 @@ def send_invoice_email(pdf_filename):
206
  raise RuntimeError("SENDGRID_API_KEY is not set in your environment")
207
 
208
  message = Mail(
209
- from_email="truevybescg@gmail.com", # ✅ your verified sender
210
  to_emails="truevybescg@gmail.com",
211
  subject="New Proforma Invoice",
212
  html_content="<p>Dear True Vybes Team,<br><br>Please find attached the new Proforma Invoice.<br><br>Regards,<br>Automated System</p>"
@@ -230,13 +230,14 @@ def send_invoice_email(pdf_filename):
230
  except Exception as e:
231
  print(f"Error sending email: {e}")
232
 
 
233
  # Gradio Interface
234
  with gr.Blocks() as demo:
235
  gr.Markdown("# 📄 True Vybes NLP Proforma Invoice Generator")
236
 
237
  user_prompt = gr.Textbox(
238
  label="Proforma Invoice Request",
239
- placeholder="E.g. Create PI for Customer, GST-18902801280128, Addresss -Andheri Mumbai, 300 candles at 20 each, 500 umbrella at 200 each, GST 5%, ",
240
  lines=4
241
  )
242
 
 
8
  import base64
9
  from sendgrid import SendGridAPIClient
10
  from sendgrid.helpers.mail import Mail, Attachment, FileContent, FileName, FileType, Disposition
11
+ from babel.numbers import format_currency
 
12
 
13
 
 
14
  client = OpenAI()
15
 
16
+
17
  def parse_natural_language(prompt_text):
18
+ """Use OpenAI to parse plain text into structured product data with per-item GST"""
19
  system_prompt = (
20
  "You are a helpful assistant. Extract the customer name, address (if any), gstin (if any), "
21
+ "and a list of products with description, code (optional), quantity, price per unit, "
22
+ "and GST percentage for each product from the following text. "
23
+ "Respond ONLY with a JSON object like this: "
24
  "{"
25
  "\"customer_name\": \"...\", "
26
  "\"gstin\": \"...\", "
27
  "\"address\": \"...\", "
28
+ "\"products\": [{\"description\": \"...\", \"code\": \"...\", \"qty\": ..., \"rate\": ..., \"gst_percentage\": ...}] "
 
29
  "}"
30
  )
31
 
 
42
  text = response.choices[0].message.content
43
  print("LLM returned:", text)
44
 
 
45
  try:
46
  parsed = json.loads(text)
47
  except json.JSONDecodeError:
 
49
 
50
  return parsed
51
 
52
+
53
  def format_inr(value):
54
+ # Format with Rs and commas — no ₹ symbol
55
+ formatted = format_currency(value, '', locale='en_IN').strip()
56
+ return f"Rs {formatted}"
57
+
58
 
59
  def generate_proforma_invoice_from_prompt(prompt_text):
 
60
  parsed = parse_natural_language(prompt_text)
61
 
62
  customer_name = parsed.get("customer_name", "Unknown")
63
  address = parsed.get("address", "Not provided")
64
  gstin = parsed.get("gstin", "")
65
  products = parsed.get("products", [])
 
66
 
67
  today = datetime.today().strftime("%Y%m%d")
68
  safe_customer_name = customer_name.replace(" ", "_")
69
  filename = f"Proforma_Invoice_{safe_customer_name}_{today}.pdf"
70
 
71
+ grand_total = 0
72
  product_rows = []
73
+
74
  for row in products:
75
  try:
76
  description = str(row["description"])
77
  code = str(row.get("code", ""))
78
  qty = float(row["qty"])
79
  rate = float(row["rate"])
80
+ gst_pct = float(row.get("gst_percentage", 0))
81
+
82
+ row_subtotal = qty * rate
83
+ row_gst = (row_subtotal * gst_pct) / 100
84
+ row_total_with_gst = row_subtotal + row_gst
85
+
86
+ grand_total += row_total_with_gst
87
+
88
  product_rows.append({
89
  'description': description,
90
  'code': code,
91
  'qty': qty,
92
  'rate': rate,
93
+ 'subtotal': row_subtotal,
94
+ 'gst_pct': gst_pct,
95
+ 'gst_value': row_gst,
96
+ 'total_with_gst': row_total_with_gst
97
  })
98
+
99
  except (KeyError, ValueError) as e:
100
  print(f"Error processing product row {row}: {e}")
101
  continue
102
 
 
 
 
103
  c = canvas.Canvas(filename, pagesize=letter)
104
  width, height = letter
105
 
 
118
  height=LOGO_HEIGHT,
119
  preserveAspectRatio=True
120
  )
121
+
122
  c.setFont("Helvetica-Bold", 16)
123
  c.drawString(50, height - 70, "PROFORMA INVOICE")
124
 
 
125
  c.setFont("Helvetica", 10)
126
  c.drawString(50, height - 100, "Consignor:")
127
  c.drawString(120, height - 100, "True Vybes")
 
130
  c.drawString(50, height - 145, "Thane - 400607")
131
  c.drawString(50, height - 160, "GSTIN: 27BV0PS7767J2ZF")
132
 
 
133
  c.drawString(50, height - 190, "Consignee:")
134
  c.drawString(120, height - 190, customer_name)
135
+
 
136
  address_lines = address.split('\n')
137
  for i, line in enumerate(address_lines):
138
  c.drawString(120, height - 205 - (i * 15), line)
 
140
  c.drawString(120, height - 205 - (len(address_lines) * 15), f"GSTIN: {gstin}")
141
  c.drawString(400, height - 190, f"Date: {datetime.today().strftime('%d-%b-%Y')}")
142
 
143
+ # Table header
144
  table_top = height - 250 - (len(address_lines) * 15)
145
  c.line(50, table_top, width - 50, table_top)
146
+ c.setFont("Helvetica-Bold", 9)
147
  c.drawString(50, table_top - 15, "Description")
148
+ c.drawString(170, table_top - 15, "Code")
149
+ c.drawString(240, table_top - 15, "Qty")
150
+ c.drawString(280, table_top - 15, "Rate/Unit")
151
+ c.drawString(360, table_top - 15, "GST %")
152
+ c.drawString(410, table_top - 15, "GST Value")
153
+ c.drawString(480, table_top - 15, "Total w/ GST")
154
  c.line(50, table_top - 20, width - 50, table_top - 20)
155
 
 
156
  # Product rows
157
+ c.setFont("Helvetica", 9)
158
  row_y = table_top - 35
159
+
160
  for product in product_rows:
161
  c.drawString(50, row_y, product['description'])
162
+ c.drawString(170, row_y, product['code'])
163
+ c.drawString(240, row_y, str(product['qty']))
164
+ c.drawString(280, row_y, format_inr(product['rate']))
165
+ c.drawString(360, row_y, f"{product['gst_pct']}%")
166
+ c.drawString(410, row_y, format_inr(product['gst_value']))
167
+ c.drawString(480, row_y, format_inr(product['total_with_gst']))
168
  row_y -= 20
169
 
170
+ # Final grand total
171
  c.line(350, row_y - 10, width - 50, row_y - 10)
 
 
 
 
 
172
  c.setFont("Helvetica-Bold", 10)
173
+ c.drawString(400, row_y - 25, "GRAND TOTAL")
174
+ c.drawString(480, row_y - 25, format_inr(grand_total))
175
 
176
+ # Bank & Terms
177
+ bank_y = row_y - 60
178
  c.setFont("Helvetica", 10)
179
  c.drawString(50, bank_y, "Bank Details For True Vybes")
180
  c.drawString(50, bank_y - 15, "A/C Name: True Vybes")
 
182
  c.drawString(50, bank_y - 45, "A/C Type: Current")
183
  c.drawString(50, bank_y - 60, "IFSC Code: KKBK0000656")
184
 
 
185
  terms_y = bank_y - 90
186
  c.drawString(50, terms_y, "Other terms and conditions:")
187
  c.drawString(50, terms_y - 15, "GST: Extra at actuals")
 
190
  c.drawString(50, terms_y - 60, "Validity: The validity of this price is 10 days from today")
191
  c.drawString(50, terms_y - 75, "Freight: Extra on To-Pay basis")
192
 
 
193
  c.line(width - 150, terms_y - 90, width - 50, terms_y - 90)
194
  c.drawString(width - 140, terms_y - 105, "Authorised Signatory")
195
 
196
  c.save()
197
 
 
198
  send_invoice_email(filename)
199
 
200
  return filename
201
 
 
202
 
203
  def send_invoice_email(pdf_filename):
204
  SENDGRID_API_KEY = os.getenv("SENDGRID_API_KEY")
 
206
  raise RuntimeError("SENDGRID_API_KEY is not set in your environment")
207
 
208
  message = Mail(
209
+ from_email="truevybescg@gmail.com",
210
  to_emails="truevybescg@gmail.com",
211
  subject="New Proforma Invoice",
212
  html_content="<p>Dear True Vybes Team,<br><br>Please find attached the new Proforma Invoice.<br><br>Regards,<br>Automated System</p>"
 
230
  except Exception as e:
231
  print(f"Error sending email: {e}")
232
 
233
+
234
  # Gradio Interface
235
  with gr.Blocks() as demo:
236
  gr.Markdown("# 📄 True Vybes NLP Proforma Invoice Generator")
237
 
238
  user_prompt = gr.Textbox(
239
  label="Proforma Invoice Request",
240
+ placeholder="E.g. Create PI for Customer, GST-18902801280128, Addresss -Andheri Mumbai, 300 candles at 20 each with 5% GST, 500 umbrella at 200 each with GST 18%",
241
  lines=4
242
  )
243