@posx/core 5.5.586 → 5.5.588
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +23 -23
- package/LICENSE +21 -21
- package/README.md +85 -85
- package/build/index.d.ts +80 -1
- package/build/index.js +4 -4
- package/dev/.stfolder/syncthing-folder-9a95b7.txt +5 -0
- package/dev/98894488.xlsx +0 -0
- package/dev/HappyThaiSembawang.csv +336 -0
- package/dev/KB/create-new-model.md +34 -0
- package/dev/KB/markdown-lint.md +14 -0
- package/dev/KB/object-clone-pitfalls.md +52 -0
- package/dev/KB/readmefirst.md +8 -0
- package/dev/KB/stock-deduction-logic.md +61 -0
- package/dev/Merchants/HappyThaiSembawang.csv +400 -0
- package/dev/Merchants/HappyThaiSembawang.xlsx +0 -0
- package/dev/Merchants/charen_thai/category.csv +20 -0
- package/dev/Merchants/charen_thai/charen_thai_xpos.csv +1021 -0
- package/dev/Merchants/charen_thai/convert.cjs +194 -0
- package/dev/Merchants/charen_thai/item.csv +183 -0
- package/dev/Merchants/charen_thai/modifier.csv +664 -0
- package/dev/Product_Import_Template.xlsx +0 -0
- package/dev/XPOS Invoice Module.pdf +232 -0
- package/dev/convert_menu.cjs +134 -0
- package/dev/convert_menu.py +127 -0
- package/dev/data/invoice.json +1 -0
- package/dev/escpos/receipt.bin +0 -0
- package/dev/escpos/receipt.hex +1 -0
- package/dev/escpos/receipt.json +1 -0
- package/dev/escpos-cli-usage.md +103 -0
- package/dev/export/xpos_menu.csv +1021 -0
- package/dev/export/xpos_menu_bilingual.csv +1021 -0
- package/dev/export/xpos_retail_sample.csv +38 -0
- package/dev/harbor-harness-deployment.md +78 -0
- package/dev/incidents/2026-04-01-reprint-timeout.md +33 -0
- package/dev/incidents/2026-05-06-searchable-field-design-pitfall.md +37 -0
- package/dev/nginx-harbor-harness.conf +84 -0
- package/dev/px-cli.md +97 -0
- package/dev/test-logs/2026-02.md +5 -0
- package/dev/tmp/xpos_product_import(1).csv +338 -0
- package/dev/tmp/xpos_product_import_fixed.csv +338 -0
- package/dev//344/272/247/345/223/201/345/257/274/345/205/245/346/250/241/346/235/277.xlsx +0 -0
- package/jest.config.cjs +36 -36
- package/jest.setup.cjs +91 -91
- package/package.json +1 -1
- package/package.publish.json +121 -121
- package/tsdown.config.ts +21 -21
- package/vite.config.ts +86 -86
- package/AGENTS.md +0 -24
- package/memo/technical-docs/01_ARCHITECTURE.md +0 -147
- package/memo/technical-docs/02_CORE_BUSINESS.md +0 -292
- package/memo/technical-docs/03_UI_COMPONENTS.md +0 -59
- package/memo/technical-docs/04_VIEWS.md +0 -82
- package/memo/technical-docs/05_DATA_LAYER.md +0 -375
- package/memo/technical-docs/06_CROSS_PLATFORM.md +0 -246
- package/memo/technical-docs/07_SIMILARITY_INDEX.md +0 -195
- package/memo/technical-docs/CHECKPOINT.md +0 -46
- package/memo/technical-docs/PROJECT_OVERVIEW.md +0 -122
- package/memo/technical-docs/TECHNICAL_DOCS_PLAN.md +0 -77
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
%PDF-1.4
|
|
2
|
+
%���� ReportLab Generated PDF document (opensource)
|
|
3
|
+
1 0 obj
|
|
4
|
+
<<
|
|
5
|
+
/F1 2 0 R /F2 3 0 R /F3 11 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
|
+
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
|
16
|
+
>>
|
|
17
|
+
endobj
|
|
18
|
+
4 0 obj
|
|
19
|
+
<<
|
|
20
|
+
/Contents 17 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 16 0 R /Resources <<
|
|
21
|
+
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
|
22
|
+
>> /Rotate 0 /Trans <<
|
|
23
|
+
|
|
24
|
+
>>
|
|
25
|
+
/Type /Page
|
|
26
|
+
>>
|
|
27
|
+
endobj
|
|
28
|
+
5 0 obj
|
|
29
|
+
<<
|
|
30
|
+
/Contents 18 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 16 0 R /Resources <<
|
|
31
|
+
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
|
32
|
+
>> /Rotate 0 /Trans <<
|
|
33
|
+
|
|
34
|
+
>>
|
|
35
|
+
/Type /Page
|
|
36
|
+
>>
|
|
37
|
+
endobj
|
|
38
|
+
6 0 obj
|
|
39
|
+
<<
|
|
40
|
+
/Contents 19 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 16 0 R /Resources <<
|
|
41
|
+
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
|
42
|
+
>> /Rotate 0 /Trans <<
|
|
43
|
+
|
|
44
|
+
>>
|
|
45
|
+
/Type /Page
|
|
46
|
+
>>
|
|
47
|
+
endobj
|
|
48
|
+
7 0 obj
|
|
49
|
+
<<
|
|
50
|
+
/Contents 20 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 16 0 R /Resources <<
|
|
51
|
+
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
|
52
|
+
>> /Rotate 0 /Trans <<
|
|
53
|
+
|
|
54
|
+
>>
|
|
55
|
+
/Type /Page
|
|
56
|
+
>>
|
|
57
|
+
endobj
|
|
58
|
+
8 0 obj
|
|
59
|
+
<<
|
|
60
|
+
/Contents 21 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 16 0 R /Resources <<
|
|
61
|
+
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
|
62
|
+
>> /Rotate 0 /Trans <<
|
|
63
|
+
|
|
64
|
+
>>
|
|
65
|
+
/Type /Page
|
|
66
|
+
>>
|
|
67
|
+
endobj
|
|
68
|
+
9 0 obj
|
|
69
|
+
<<
|
|
70
|
+
/Contents 22 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 16 0 R /Resources <<
|
|
71
|
+
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
|
72
|
+
>> /Rotate 0 /Trans <<
|
|
73
|
+
|
|
74
|
+
>>
|
|
75
|
+
/Type /Page
|
|
76
|
+
>>
|
|
77
|
+
endobj
|
|
78
|
+
10 0 obj
|
|
79
|
+
<<
|
|
80
|
+
/Contents 23 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 16 0 R /Resources <<
|
|
81
|
+
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
|
82
|
+
>> /Rotate 0 /Trans <<
|
|
83
|
+
|
|
84
|
+
>>
|
|
85
|
+
/Type /Page
|
|
86
|
+
>>
|
|
87
|
+
endobj
|
|
88
|
+
11 0 obj
|
|
89
|
+
<<
|
|
90
|
+
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
|
|
91
|
+
>>
|
|
92
|
+
endobj
|
|
93
|
+
12 0 obj
|
|
94
|
+
<<
|
|
95
|
+
/Contents 24 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 16 0 R /Resources <<
|
|
96
|
+
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
|
97
|
+
>> /Rotate 0 /Trans <<
|
|
98
|
+
|
|
99
|
+
>>
|
|
100
|
+
/Type /Page
|
|
101
|
+
>>
|
|
102
|
+
endobj
|
|
103
|
+
13 0 obj
|
|
104
|
+
<<
|
|
105
|
+
/Contents 25 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 16 0 R /Resources <<
|
|
106
|
+
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
|
107
|
+
>> /Rotate 0 /Trans <<
|
|
108
|
+
|
|
109
|
+
>>
|
|
110
|
+
/Type /Page
|
|
111
|
+
>>
|
|
112
|
+
endobj
|
|
113
|
+
14 0 obj
|
|
114
|
+
<<
|
|
115
|
+
/PageMode /UseNone /Pages 16 0 R /Type /Catalog
|
|
116
|
+
>>
|
|
117
|
+
endobj
|
|
118
|
+
15 0 obj
|
|
119
|
+
<<
|
|
120
|
+
/Author (\(anonymous\)) /CreationDate (D:20260123152929+08'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260123152929+08'00') /Producer (ReportLab PDF Library - \(opensource\))
|
|
121
|
+
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
|
|
122
|
+
>>
|
|
123
|
+
endobj
|
|
124
|
+
16 0 obj
|
|
125
|
+
<<
|
|
126
|
+
/Count 9 /Kids [ 4 0 R 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 12 0 R 13 0 R ] /Type /Pages
|
|
127
|
+
>>
|
|
128
|
+
endobj
|
|
129
|
+
17 0 obj
|
|
130
|
+
<<
|
|
131
|
+
/Filter [ /ASCII85Decode /FlateDecode ] /Length 337
|
|
132
|
+
>>
|
|
133
|
+
stream
|
|
134
|
+
Gat=d>t`'h'Sc)J/)JGu1aZAR-/SIB6uc^&]Idt?'AKgq(&%)Zl_J=Za+YcaO>hsX3CcE_P3n\"^no*)Xb9[CYE52gp]oTI?%;k9KCffb+*6X1IGpt4:bq=/qXGG,;l?c^N4T+k!dXXg+),8f&\3I-G-~>endstream
|
|
135
|
+
endobj
|
|
136
|
+
18 0 obj
|
|
137
|
+
<<
|
|
138
|
+
/Filter [ /ASCII85Decode /FlateDecode ] /Length 391
|
|
139
|
+
>>
|
|
140
|
+
stream
|
|
141
|
+
Gat>R>t`'h'F*LmregGJ"_3aF,eJ1o(i8s^n@EdUZ`[#7(T77ZdnZe.U.>QCdNn;!BhgZt5BoDZ%</:dEQu<'oFa8t.J+#^*PFr?H`'i).;)84PX"+C=KHNd1GCFNWB\HlRd`_>>t$r:*rmD5pi$8:\o8h(kS%5aeHEM&fG[9"D,6j4T[,[kcB#^oQbVIg?T;Z9T(+\VI1:OX1Yh>qh,X)2"QZ=S5VPmVc;l7MWc/.(G!YdD,t"ZmLRM4t0QknC_okB[[.jLSf_4q7T'F2?!k5;f;!TSL]WaL)75KZ'B?N0Pc\5L6k#D24C>0/;_>TKP9[$^.D;cs[#&DA5HUN$!da;fji6f"!AShYk1(3L_`V_C>Che!4BU?h2dChOA&Ua]XYjO=~>endstream
|
|
142
|
+
endobj
|
|
143
|
+
19 0 obj
|
|
144
|
+
<<
|
|
145
|
+
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1290
|
|
146
|
+
>>
|
|
147
|
+
stream
|
|
148
|
+
Gatm:D/\/e&H;*)ESmc3W@H?%?M85O[\6o\UlAC`=GD?gkZEfi3YZ/Os*i!)fhuS..52%5ki:J^O7%7:+qW[$b\peir05%0?O.2Mfb-j>LQHq;T<m5"N&]'AJN+p(P,&Cfi.`=!&WIUb':OYhkgOKGjtXr4)1!NS!-D59Esa2?c7.;H4lXB0L<E<5\$$tP$J?@-pBe#8A<b@EOFc)0%R;7sJFiq2h]V]PR&1I5D8FCrAIF45]:(Yc$JspN]]UlF'o*=E7[,og=qk8]@4fHe93'uSj;$Q+1f+7rAqQsIm\&CDMUs7Qr'<)<M?_]2.g-d97pML[#$E2KFa\B7mtF0m'iRaKCEG<rQO_\o<0H@(/[VV-bTpt-Q>?4HYbhEO.8_<pcDkUa2/ChHDb%$<X$lpmaI-*]Cfe:6&^Z.7B4V*1!f@=kpleR9aP!MUn6"1s+4)uBL`uCPhi6X&(AP(%;-nAKs'[""Wba@-(kWTEd3r$RAb2QN;09@c1b?2u%DKgc4E[$P;XJ;);!VR1!_d(6/@gEK`iRVcj5>KJ&\`a-@f&o@PW4!`XQ::mk)^G!</7!j)$YFK@O1p08q[!;6[?cfK0Mc?/%G7939fhr.40spEP>nBV9[6geY(A<QOHq!?Xe=tXlT<i[n:-!3aCW@5&sn(a!i0\1e3EHI:9>uD72]^3JFqeCWna$:1*rX<km>-&DCtcNThW\!M<4+'7;.C4JdCW+F.QQns9R<k:WW1hA5)EH(.$JRIRn\A8^*[L%LI$%/qC2&Wh:<PO;`2rjEakBqRu#WdE!,\)t`=BFt%GO-c`7Y''.;lGJ7P:YHL?gIf,]/1+IZ,s6TXes!b:k+>BDeDWtOY]8EpYV"B4?=A!6]u'2hkPF)rINh>-8F]O>DMjZW43u"tIdUDX1gPNjN-#5'69N!Leu,HS]iY^!Mb[2jC9E#(-F>?LVuOJp;U<9o/aq9&>4^kXY&S&Y^_`\o\]XkJlP\*/!om(cnd1UWX7'R@;>]g3VJ&#C+/NL0&Bj%2\FnX1^mQXOi\\'iZo\ef-s+'k4%%D0^RV#S\?tZ5_q**\r3pbM(Ne2q0lmCW!c!du*n,=,SL3RmE&:7SVc1d])FkdJf:su#fWs(U&=cFl0t*\]PAHs0ObqA'Yt+s5H><A4pXbh*`5*Wh!(Vd7'59_[Lqr1\BDqt@q]Sh+k,;>rg_ab`AALeL%!:k(\!D??3%;$(dU8+K*-BZd6$g>_WF0;Ik[c^]X:,oYCu3(R#:Oq\*^c]:&H+_qU]HKS,Ia_&kEEGap)g]#%o!~>endstream
|
|
149
|
+
endobj
|
|
150
|
+
20 0 obj
|
|
151
|
+
<<
|
|
152
|
+
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1877
|
|
153
|
+
>>
|
|
154
|
+
stream
|
|
155
|
+
Gb!SlgN)%,&:O:Sm$jQ)_[ms09A^XHmAN`kE`".o9h.QJONj.:'G??pJ"O]>'I=EWdlqn2[UJ*%pCQ+cS.u&H5=GIq[fETInFCc)KYh1#,kOE*]Re[mMf0EM+QEUaOEa?[-Fa-o4?n[949LYk1rP@p%0I6]:u>TP2:=KM;d2(RJL&Z'V:,!9i5*)&cAS4/S"a@G(MjItq3mT<j_A\!jN<g%cX?8X(=+:tp\5V(,4P[[?S*62TUGjESZeV['6p)0G>Lo`KlgV,?-O9gM$>`feU638!ZGCJJQEu^&7sKX']MmeK2>nC6.B%d?W=M=c:$fS;GDr`"irV2KCb,-P/O+$_LTK2r2CQMSte=AZ-,G3QgnJVZBrRQ(8cV3UG+%i2t;NbFrNtoZsd@'+K[$E,Dklr(XXVa$]cBCBTF4`T<dW%5L@2PHH^iT>^ZH/")G4j^5Tl+/k2809VmjETo?g2&FU4X0U<<#ordF=<f?oB:`eOc0_9VW@_ZE6j![Vbg>RKj9jSVt;=NiNI[G=7d9Pb29#n!@=?CdG=<hQS/giH-r)=Eg>"mC&->'M3=:H3UX3Bsor,FOVbZ?[e8n+)b`!l>J@KuXZD-5%4/00<m5.8&4.$5e?.(SPDgjD9me8-[TJHU#-h3C[$b/[Rj)6u9&CU0IiP)BralW]k;=h=d[@]O[a@^VDn6,#9YJSu7=7[;q0/i-kT;cSW"h^l5fCYF([Q45.HjV-NHKtW1Hg./gZ%'%JU<nuPgb>D#c$!o_e-P:tmj(o-EY0R4;b$_e67"30+]J*oqg]*_JfF9IX+ZH-jE`_:4js4SiB94o?bWl3OY'bBWc?)I!WoL?u=+Z@3FAXd>BdO%_"GJ]Afg]@(=;O]W>J/h5pr(:)cZ7"o:ta:bF7G17%<2Iio$`Vr#t?ri[sY=#gB^P110CTPDU<rVqY*3,G%T3-j1lq4WXJSWM]2@X"XXfg2Tdi*Tis9GX>;BpU1p8Nc:aI'/oCmhq2QV`qEVM);QhN>[]YWkq/F,b3E95lrX<fOBusLa];uc''+DY'pSp,e::E*!G>hR>FQNb&EB!0!P[TdhjsiFE-8k(\q"+0q94EC:,_,qrX4#KFBggms*uEcB*]cm(<RO9q9),>sI5C$sB)Dai?9R*n26<4b;)_-/0;QW\IB$rAW1-M_>rh1^;1"a7*-JT]od32b/@F?qW13HADgq.,[Ed4W=);0]]r.9@2!b)CekL,AV/p*o?n6cr=LunTrj4'_4FN*5A67W[[RHnihfW\-lI<0lGk$?MR:'eag(MSZC=d"7^P'/!1NMjMfn0!3Ha\,'b,N4YW^KleO#:EL^WL[$D1bg>X\=t^J&<auZ_kR<l#)L)i[8h`KY"b&dq3<oHT>f4LA"B2V($a=mF0G=8[C6^n"b@MQof#J-ENcF:BPA[c-g-8qXa(f8V;*<-VC3,0j'3k,b^QH\:<<u=ckgq7;-!3k.Drr1P<(%PIeB;*bn;i2tsG<L4r@N0Z]<,kP+H^@lp*b8l=_u4`\!EJmN$]36Z=JcKHZ6(PB*F`bE(%b)N4bA8mR'edMuXVQFX5op'5>fm!,VfU?Z_[M6K,5;)Pe&k9,NS#7QN`rrJQB$bFpUR2q[8+ji'.pu:f,A5[[lI##o4$Q4u/)LQ@:o_(p%)JX`4!gEg`d%Ui2GM%<CRHQuW;;e\n!&fje?$ku]H;p^rKoP[U\-t&#mV&%<F$%0e_[a[fC3PT=nS`FT_.,!=B0h[9]Y%I4elPcH47d?@(T'][(0!Q%Jn]F=]&e3P/2=gB"5gb`@c3G+1Jm(m68b;m"sQ^%=:%EB\<(s070gi%p_=q//-aZ=?8%"43Tr@+7/p_8R4)-Jl[s/B;L'/Y=2#g26n`9lGCLNj^7dG~>endstream
|
|
156
|
+
endobj
|
|
157
|
+
21 0 obj
|
|
158
|
+
<<
|
|
159
|
+
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1590
|
|
160
|
+
>>
|
|
161
|
+
stream
|
|
162
|
+
Gatm;>Beg[%"@rS^g!>3<CMbUope0]CY!&E?#F!F;"*a!&oBGR`?h@Vrq?LDb*MEc\^X-iOTL+7L^sId!]E51s%RNa?RP@BG65^6aUAH_-qqqEA2=!>,).lZ+eZa\_^NGD\7"!`mfX\6`/,m[ZlJ?D2ZPQSTes&UJ>.&umJmQYE5thMa.<Id<LNai^FeGScJUKOn:Sp^(aaG\H)$U2ij3J%#s&:CH=9mGe#:0jf_R.Y]#fPT>fhlb&D'pM`t_D0/6bE1hUSop^l*P?<2E^5_h&Wghi]@sVi%NY6DBI(erXm#k\b[d$U]]D=)9h41L"bTdsqjOLMCltiuJ17m2tA[a_JqDQ&!<K?*qBTKBM2li3pu;l8nDEItGN'%8'Q[=nQI7MY8m-<YRM7N<%l4***cRK=1W[^Vdf4%B2Lk3pmuD2GiAs3Y(_s&!Vjh(SY.i;6;lSkcbZkSmgFE</8R](_"k`-544^'ebITD`==g":\I%FdS.K#1UCF:]%7LN3X)/R.o?HfWNkc$!(Hl<@(T]ZUt8R1>HYSYXarhNfKq*[lQ,k53+*/=Od-u'E&[a[S%i@)&0"5HWf6,,#0VTM)*-D^_#n>aO^La1?C=;KH*7[!Nm+)Vg.R3rsZ]AGC2<C)`-1nmHd_"ngFG)7TLA)KETM[-7L..6p?1&:F"&8LGgAt*N/[p_6p/nL1G4"ljZUo\@3"?VkN'>964>5mY26.U7pO96R-cQ"fh<#TL4*$<,Y.:goiu7GK:-PQid5KV0/Lf"fC<Y;\h%G<K?0]H9>.<4o6,Ig4-2/]9[5269U\u_"`hfa76#PEs^$WVf-^,R$t0m\V)uD3\b`rAMkKt?#O-eB9GuMZ+T[=!Jt3([>>W&BmODr[c:hIBXN>\I/7DpH?3PIbN'CN&n[s?e>I@dJh47X!d27#4g21oYNLt#-CXo710uYM2bURXnb;^b2$86HW]B-N)B9B2f3j'Hcu'HaTRKi1F;T&`7Sc?Gk[T]Qb74U"M`Qr8R(KT_8'2D<6(BH&/!pd]E,g>T\q+kUh]5Cq\d<P@<Jp%F9&#_O]+ib$(>(5Fm21RibqroZpm`.?ei!jGZ%Sk[[/4O@=.m!G5h)_eo'^.IB83a5cBBF=mT-7VOu=dm=(eL4:<2S`S5kJF!<m5sRYc@oikqDirbDrcQ;Sn`[+.L-omG.krV+jjf;`sE9F8AK0gqhJP?77pjhJ"IH*']j13b\aXlbj46W+<Y&=N`PT:3YVM6L&]*];#fX[.&=&<!fH[^GBk)?_>3Gnu<S:quVefY`R)!Y^g!DV]iFdM=ElDe%JX*@mrk**dk!:9\qO9m%G72ocC<QK:;*"JuZ9lVd?81_=@k#QD',!FjNOE?K4,2@f)5%pRKUL4q"NrQ+aFHFd+hM6jd+/0LP?ErGK0i7hG\q2uKkapcb`k$l6U7^Y3)Y`rWSB0DQ`a,]cN&*u@R2&Nd)L&r.(>Lp[o[WT8^MNYS[;r+(Gpe+K0nP8Fbmb8UE?&/U^fJ7VNX0Go:rR3Keh"_qRhGfm8[Ck%D]WB:XO`"??S*kCk)bRTAhrWUa#;A"R&q1gnqkpgcrpeh0D*!(^r==JJ@-%~>endstream
|
|
163
|
+
endobj
|
|
164
|
+
22 0 obj
|
|
165
|
+
<<
|
|
166
|
+
/Filter [ /ASCII85Decode /FlateDecode ] /Length 944
|
|
167
|
+
>>
|
|
168
|
+
stream
|
|
169
|
+
GauHJ9lldX&A@Zcp?6%a6k\Ct->bg21c%ne[$P#OaK?/.*"^6%^V23$2R[+OdnE76@TIEDAbWkA$3UU9mn!_&5(U%5$C&A;#1U%dR!a\8'C4&?%5O`fmfXd:/?m#G$IPt^eYAL93J&4m>_g-#)FD8KG&#]-^cXOWS:*>h3/>G/=Go])2P/E2iG'$$<(Fip=O>\)ird1)1Dp(T,!CM!4\SUp+OhVfk\L/C7D])o.8So?U^AiKo:gn9alEI5a17"kO?&+74NAb,A!,W=]Y(S36Z5&H=!,tHXE):<__E"p7>46B]Ds6d)6Y/`OR.8XFFM-nhQk%LG[UlFBqGPM-B?HoVsr`)AUEoOB9,;iEk[&C2!@VJPFgAqZ8RiQ"W-o0=92pL#@jO3>Pm]r!FFAf_[%N?+#t>sfK*Wg\]o5]);'ru].#.6;m*:Gfd36PeccP+JOdH!i)XHReWOF!DKDL:cJXT#AQA6%>&8uA'%<c8<\-u-.#V?+ONh$c3^HD%^S)]TL3i;]jRmCJO]`cgES$Z#nY5#u=k'*.nM2Q3;DY_54^L%-<LbS5<l<?_4G-Na;OFV[otp:Lj;elS1DfZ?C.K7I8*mc+V[:-"YoiZf^aNh)kpiUkGJ$Zhk8/7"+\CQe-Ou(CR9#0m=rGfno)A<nTJ#IB"splT+&GEp%OjcK#4/kU4L]7<s/B2Ek\F_`_Z!]IOjV>Il?Z:;%J3-/Go@JJ/.G\S<.%6K8/ZKuY6"GW_"#_LIB(?55YQ:4Gf!6s:N+n&J^@-NDNmd*q%M*EF1jWiI9^Bu\MBYsSsJ(rXckTDnoQ&G3FpZ,M<2UE>TPr*l&o+''f483(U*uumk^.2,k[4Lp_3+8j2M%Xj<:,hjfAi*?g5hK<4@IVfstXtRr0q9G1uW=]q_iq9p^iCBXC'7>;.s_BPf/GA&F)2GN^q+e"X\T$Ylf)2Z~>endstream
|
|
170
|
+
endobj
|
|
171
|
+
23 0 obj
|
|
172
|
+
<<
|
|
173
|
+
/Filter [ /ASCII85Decode /FlateDecode ] /Length 835
|
|
174
|
+
>>
|
|
175
|
+
stream
|
|
176
|
+
Gatm:>Aoub(k'`6pthH5.`5KmoPr\67G?Hj7I.iH9eMgO/(gigUs6[3LS69T`=F4\MLod0pmgs/&Ff35p:"]8hW=8#,68EB?3,nq#kCd\+eLkl83\k$+c2#AVHJd4Yf$(]H'#u&OW]R$U)-3@#np.9$*H`%!*lP?`<.5s]6Fmo92?33(;d;@U4u;AG>^S'[="E/rY+i]/bBj,?GlZ^/,2(uK*Yoa0Z->m0UYE;EPP(fDeT_Xi*)C>_51V,JdZjq`WsT!mJ%>Bk\s?chr8b4>Su%*TOl5;0G:nJ8R=*L\R6?$5oWrh!7u14SHGuD<YufI%@B"2C-u(HEtXDQ#pfo)P)eE/h)A"E-ZkH0eDtMl`/>6Lma5i.'Pf1!-jaX1G!gP#G2^C5a't[Ym>L$O.hlcCM2L3Z07]fthW'n/_p'5PWIWB*D67huM:;!"50K*j*(-\)8#f#X:$NUbI`BasUc';j%?l;nUtQ@1:rQh[PifP'\)YsfG0AC<os#HIj),T<:7!aa[kL!SEl0'2?R7@r\0O0`[M7G;%X!_ji?'UgB3_3J/ET6''uQ+]<.6jhiZKK\[<=#?-0*CV7D/r;g!guD^akM(YIL5_oO[nfF^M=I#4ZpBJ!UGC[2d\8_NO8=&W:&ECnrggG&S`:dk9i<99+,lYj/0L%574^TQQ50J<5"=Lm=/h17l:.<jO\fh'KJ!QJMEKqu6![Eb7l\Kuq50m6TFV!(]3llqQbG-6>tL;E"9tSiXH8mmAh"eV-DL";V2T[\:cse]n7P%$.f18*b$\P1VZdThSLND)3iEkq;po@QVcT.!,niM?:E.^V2a:ndFWUCdA~>endstream
|
|
177
|
+
endobj
|
|
178
|
+
24 0 obj
|
|
179
|
+
<<
|
|
180
|
+
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1617
|
|
181
|
+
>>
|
|
182
|
+
stream
|
|
183
|
+
Gat%"D/Yn7&H88.0sYn!Y,XJto9sT7e)\ri[W/I`<Ya?(g>3$9#F6Y'WW2p/(N9?nk>%=Q1=*Tq1W.Kh!>@[cs,Bp,[o<(^L]F\f3Wih12GDHTrH.LWO49Vi&"ZK(h[b+A5eo(%fCBn8L_\QUBNXt]9K^DZHg$Hj"/)\/SQe@,jkR/+l[@Z@G9U<>9JSTL*X%N@]RQYJniKNp_"<m!rt.J$$Du:H?tu>VOOOjs*E/l2,<CSFqHa`KoF^ep$O/DL,iXr!F5R3uL`/1aB(P!XncB/dQ]Wh?<6mu?OqeU=.d:`)=Xl1[WDkK`Ou0O?*<EjX9T"3Tc<3I[Md`$3--ITI`PJ\c/^Ia9ptJfoAg1$M6jHdMVctss/8aBI3D[Fh.0mH(s"n`h!CeAr7gC_7?K"=lZhf&*kC9(p')V=Bb&4;9_pReY2T?1VK<AI^Q:sk[T!=Ai?*KDiQA'oFdlpgh8g.o#-uf[!K?\J+`ShpZo-+fY-C*b4m@4`GBG-"..4fCj7ILA6BCQ"i#3fQUBiGjGUjYo'I>2Zs;P>ngno1-i:N]t"3$dIP1HE45J"M3BG^=Z88US(3UR(b[n"'@hXQ?nG)OF#2H?!n+*&!W0r!29&j2^E_cc&>F!gbGI)^Cc*jDFEunal\'g!([G?!N8NS'0,B2i<gZ]=t-,.XoBK-tZo1BRqNr\\0Y1-FmCS]r^>ZW!D^'Hj^C%eVh_iVUkq(FW5\e<5a1(=g!\XI7mR:hQ:Xq])T$3BT^\A(ZCf0l9)EK*23UtmT9[S>V89A'(AUraJO*"=)K!*%G]Ao&i&,N2Xfhr.NGC8X,!k:DhaN$]%a<S6:;H)"X6f[WH.;Idb@0D_pQO`FEeBiq@p4_Q7eGh),6!j=p<(kOEMHmBKg&^)=2*X$P"C$[<%IO2P=14X6,r1s5EeK_I;$jb+72ZMaMBQ>aOoL;9/7ZnEGq#g)=t@5/gfP8QR@AB3-26ECa*P,KmHaZ"]7nIqm[B[X[61L1=Fp]-A$A;TrH(=^.sflRgTA0t(RoooA9(KWH?)iuR$dCu"F?,f%ZA9K^r#aGq9Zl](5^4.a5:>H.R-m6Wel/;Xa>oZMo"\2cu9\-dXn\.c:?"3&<%?bCJkap^;a'+;hT<BL'm\l54>%a58KIMHT#T&nT*i31GL#^hnNU[M>KrJihWrdWcOmOiE+*C0;RbYgO9\]\Bc>30tu"u&m`f[o5=.k2@;ZF4ZYY.E:Dg4.MGAD7j(\8/M8m[^.H)Te:6q_k2:><=6B-^?K99"p_NNFl>\,$N_0Np'p>[H/:PmKucF5mgF3n^a%ZSr&O7EE=d%qf'A/]6p+4]8LTE6\]m:)BWa-MA71H]O=jmbDM\.TUm&hjD:kiY.(BSENDlYVk^3AMMF1Hnl,dmfr7&1RHUsb.?b,ocpAPHb\#$-m:7$Lg<(CI<bo>uo,MPqFH\kl"N3uXH*F:&#iQ]Wg!lAP<!lQHMZM?6//5Yu]d[+@Z;\*s"+Gad)8<!Q/)Wk2RV%+B!gg^#j#t;2a7UiRoghnT$!_8FM))C`;7JWZ=(.(6BLK\Cb\KOic^$Q@.N(E,17VU7$m-B*cD`k+?cr_X+Bq@=St>a[R&cCNha[H>Iph_P#:H`H~>endstream
|
|
184
|
+
endobj
|
|
185
|
+
25 0 obj
|
|
186
|
+
<<
|
|
187
|
+
/Filter [ /ASCII85Decode /FlateDecode ] /Length 554
|
|
188
|
+
>>
|
|
189
|
+
stream
|
|
190
|
+
GasIdgMWN8'R]'ojtVipC)\kh[;&mEC8G6]MkZ#XM4?-,!H5":q]96#g"kd7"1I?NLV_3n?o7ZLC^8m.N=VXA\3Sl&k@otQ)_UF:+lSQ_GUg$tTk%0'<2F9_;Y=P29h$<A3V#l%4]e"-qXYu78l=XaBU3qcBiZdc`1EUC'E4+4TuU0A32((D>:j/MF@tKJ.`i;ai+umm4^YLqe2TUeDYHZ96[aadr+#nrreog6S)*bT[MbWU]B@kA>.HjEgML."09)jtFmI7r$St!3dFi4=2`9tc6-h&QM#>oh?ougV4jC/JNLMQ(?M50ahkM1h!R,48ZON4BLYbdnSSdh&ng500:2dMgh=Y7I2-9?C;h7gCpWnZ,d?urbBMD"3RQO<(1Nkklnj3.FpK`r^OqgT?1+5Ro:o"r3,fd)A8Qt'@OrT3"UiD%E"S`[VaNlBX<f!j`(&qc@7KZ,<4[t8:(bGg/$UlH$'uOc$&7j>/IZE@4ANVgcooq#6[AQbmOrq%M8V\g<_4Kh^FL28n]QD-G`Rr$#pgi.H5_2$W:i?BV5?_^m0E~>endstream
|
|
191
|
+
endobj
|
|
192
|
+
xref
|
|
193
|
+
0 26
|
|
194
|
+
0000000000 65535 f
|
|
195
|
+
0000000061 00000 n
|
|
196
|
+
0000000113 00000 n
|
|
197
|
+
0000000220 00000 n
|
|
198
|
+
0000000332 00000 n
|
|
199
|
+
0000000537 00000 n
|
|
200
|
+
0000000742 00000 n
|
|
201
|
+
0000000947 00000 n
|
|
202
|
+
0000001152 00000 n
|
|
203
|
+
0000001357 00000 n
|
|
204
|
+
0000001562 00000 n
|
|
205
|
+
0000001768 00000 n
|
|
206
|
+
0000001874 00000 n
|
|
207
|
+
0000002080 00000 n
|
|
208
|
+
0000002286 00000 n
|
|
209
|
+
0000002356 00000 n
|
|
210
|
+
0000002637 00000 n
|
|
211
|
+
0000002748 00000 n
|
|
212
|
+
0000003176 00000 n
|
|
213
|
+
0000003658 00000 n
|
|
214
|
+
0000005040 00000 n
|
|
215
|
+
0000007009 00000 n
|
|
216
|
+
0000008691 00000 n
|
|
217
|
+
0000009726 00000 n
|
|
218
|
+
0000010652 00000 n
|
|
219
|
+
0000012361 00000 n
|
|
220
|
+
trailer
|
|
221
|
+
<<
|
|
222
|
+
/ID
|
|
223
|
+
[<c03ca4c5a887abb2e75db5d4579bfba7><c03ca4c5a887abb2e75db5d4579bfba7>]
|
|
224
|
+
% ReportLab generated PDF document -- digest (opensource)
|
|
225
|
+
|
|
226
|
+
/Info 15 0 R
|
|
227
|
+
/Root 14 0 R
|
|
228
|
+
/Size 26
|
|
229
|
+
>>
|
|
230
|
+
startxref
|
|
231
|
+
13006
|
|
232
|
+
%%EOF
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const root = path.join(__dirname, '..');
|
|
5
|
+
|
|
6
|
+
function parseTSV(content) {
|
|
7
|
+
const lines = content.trim().replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
|
|
8
|
+
const headers = lines[0].split('\t').map(h => h.replace(/^"|"$/g, ''));
|
|
9
|
+
return lines.slice(1).filter(l => l.trim()).map(line => {
|
|
10
|
+
const values = line.split('\t').map(v => (v === '\\N' || v === 'null' || v === '') ? null : v.replace(/^"|"$/g, ''));
|
|
11
|
+
const obj = {};
|
|
12
|
+
headers.forEach((h, i) => obj[h] = values[i]);
|
|
13
|
+
return obj;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const itemData = parseTSV(fs.readFileSync(path.join(root, 'item.csv'), 'utf8'));
|
|
18
|
+
const modifierData = parseTSV(fs.readFileSync(path.join(root, 'modifier.csv'), 'utf8'));
|
|
19
|
+
|
|
20
|
+
const items = itemData.filter(i => i.is_delete !== '1' && i.visible !== '0');
|
|
21
|
+
const modifiers = modifierData.filter(m => m.is_delete !== '1');
|
|
22
|
+
|
|
23
|
+
// Build modifier map: item_id -> group_name -> modifiers[]
|
|
24
|
+
const modifierMap = new Map();
|
|
25
|
+
for (const mod of modifiers) {
|
|
26
|
+
if (!modifierMap.has(mod.item_id)) modifierMap.set(mod.item_id, new Map());
|
|
27
|
+
const groupName = mod.group_name || 'Options';
|
|
28
|
+
const groupMap = modifierMap.get(mod.item_id);
|
|
29
|
+
if (!groupMap.has(groupName)) groupMap.set(groupName, []);
|
|
30
|
+
groupMap.get(groupName).push(mod);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Derive category names from first item in each category
|
|
34
|
+
const categoryNames = new Map();
|
|
35
|
+
for (const item of items) {
|
|
36
|
+
const cid = item.category_id;
|
|
37
|
+
if (cid && !categoryNames.has(cid)) categoryNames.set(cid, `Category ${cid}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Override with heuristic names by scanning item names
|
|
41
|
+
const catKeywords = {
|
|
42
|
+
fish: /fish|garoupa|seabass|stingray|fishcake|fish cake/i,
|
|
43
|
+
squid: /squid/i,
|
|
44
|
+
chicken: /chicken/i,
|
|
45
|
+
prawn: /prawn/i,
|
|
46
|
+
pork: /pork|pork neck|satay|skewer/i,
|
|
47
|
+
vegetable: /vegetable|veggie/i,
|
|
48
|
+
soup: /soup|tomyam|tom yam/i,
|
|
49
|
+
rice: /rice|fried rice|hor fun/i,
|
|
50
|
+
salad: /salad/i,
|
|
51
|
+
drinks: /coke|fanta|sprite|drink|coffee|tea|juice|water|mineral|beer|wine/i,
|
|
52
|
+
omelette: /omelette|omellet|egg/i,
|
|
53
|
+
seafood: /seafood/i,
|
|
54
|
+
dessert: /cake|ice cream|dessert|pudding|waffle/i,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const catItemsMap = new Map();
|
|
58
|
+
for (const item of items) {
|
|
59
|
+
const cid = item.category_id;
|
|
60
|
+
if (!cid) continue;
|
|
61
|
+
if (!catItemsMap.has(cid)) catItemsMap.set(cid, []);
|
|
62
|
+
catItemsMap.get(cid).push(item);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (const [cid, catItems] of catItemsMap.entries()) {
|
|
66
|
+
const counts = {};
|
|
67
|
+
for (const item of catItems) {
|
|
68
|
+
for (const [label, re] of Object.entries(catKeywords)) {
|
|
69
|
+
if (re.test(item.name)) counts[label] = (counts[label] || 0) + 1;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const top = Object.entries(counts).sort((a, b) => b[1] - a[1])[0];
|
|
73
|
+
if (top && top[1] >= Math.ceil(catItems.length * 0.3))
|
|
74
|
+
categoryNames.set(cid, top[0].charAt(0).toUpperCase() + top[0].slice(1));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Collect sorted category ids
|
|
78
|
+
const sortedCatIds = Array.from(catItemsMap.keys()).sort((a, b) => parseInt(a) - parseInt(b));
|
|
79
|
+
|
|
80
|
+
function escapeCSV(val) {
|
|
81
|
+
if (val === null || val === undefined) return '';
|
|
82
|
+
const str = String(val);
|
|
83
|
+
return (str.includes(',') || str.includes('"') || str.includes('\n')) ? `"${str.replace(/"/g, '""')}"` : str;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const headers = ['Type','Name','SKU','Price','Stock','Is Internal','Min Order','Max Order','Image URL','Thumbnail URL','Sequence','Item UID','Print On Receipt','Discountable','Open Editor Panel','Is Delisted','Name Translations','Prices'];
|
|
87
|
+
|
|
88
|
+
function makeRow(obj) {
|
|
89
|
+
return [
|
|
90
|
+
obj.type || '', obj.name || '', obj.sku || '', obj.price ?? '', obj.stock ?? '',
|
|
91
|
+
obj.is_internal ?? '', obj.min_order ?? '', obj.max_order ?? '',
|
|
92
|
+
obj.image_url || '', obj.thumbnail_url || '', obj.sequence ?? '',
|
|
93
|
+
obj.item_uid || '', obj.print_on_receipt ?? '', obj.discountable ?? '',
|
|
94
|
+
obj.open_editor_panel ?? '', obj.is_delisted ?? '', obj.name_translations || '', obj.prices || ''
|
|
95
|
+
].map(escapeCSV).join(',');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const rows = [headers.join(',')];
|
|
99
|
+
|
|
100
|
+
for (const cid of sortedCatIds) {
|
|
101
|
+
const catItems = catItemsMap.get(cid).sort((a, b) => (parseInt(a.sequence) || 0) - (parseInt(b.sequence) || 0));
|
|
102
|
+
rows.push(makeRow({ type: 'category', name: categoryNames.get(cid), is_internal: 'false', sequence: cid, is_delisted: 'false' }));
|
|
103
|
+
|
|
104
|
+
for (const item of catItems) {
|
|
105
|
+
rows.push(makeRow({
|
|
106
|
+
type: 'product',
|
|
107
|
+
name: item.name,
|
|
108
|
+
sku: item.codename || '',
|
|
109
|
+
price: item.price || '0',
|
|
110
|
+
is_internal: item.internality === '1' ? 'true' : 'false',
|
|
111
|
+
sequence: item.sequence || '0',
|
|
112
|
+
image_url: item.image_cdn || '',
|
|
113
|
+
thumbnail_url: item.image_thumbnail_cdn || '',
|
|
114
|
+
print_on_receipt: item.is_print === '1' ? 'true' : 'false',
|
|
115
|
+
discountable: item.discountable === '1' ? 'true' : 'false',
|
|
116
|
+
is_delisted: 'false'
|
|
117
|
+
}));
|
|
118
|
+
|
|
119
|
+
const itemMods = modifierMap.get(item.id);
|
|
120
|
+
if (!itemMods) continue;
|
|
121
|
+
const sortedGroups = Array.from(itemMods.entries()).sort((a, b) => parseInt(a[1][0]?.group || 0) - parseInt(b[1][0]?.group || 0));
|
|
122
|
+
for (const [groupName, mods] of sortedGroups) {
|
|
123
|
+
const quota = parseInt(mods[0].group_quota) || 0;
|
|
124
|
+
rows.push(makeRow({ type: 'modifier_category', name: groupName, min_order: '0', max_order: String(quota), sequence: mods[0].group || '0' }));
|
|
125
|
+
for (const mod of mods.sort((a, b) => parseInt(a.id) - parseInt(b.id)))
|
|
126
|
+
rows.push(makeRow({ type: 'modifier', name: mod.name, price: mod.price || '0', is_internal: 'false', print_on_receipt: mod.is_print === '1' ? 'true' : 'false' }));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const outPath = path.join(__dirname, 'export', 'xpos_menu.csv');
|
|
132
|
+
fs.writeFileSync(outPath, '\uFEFF' + rows.join('\n'), 'utf8');
|
|
133
|
+
console.log(`Generated ${rows.length - 1} rows to ${outPath}`);
|
|
134
|
+
console.log(`Categories: ${sortedCatIds.length}, Items: ${items.length}, Modifiers: ${modifiers.length}`);
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import csv, io, os, re
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
|
|
5
|
+
ROOT = os.path.join(os.path.dirname(__file__), '..')
|
|
6
|
+
OUT = os.path.join(os.path.dirname(__file__), 'export', 'xpos_menu.csv')
|
|
7
|
+
|
|
8
|
+
def read_tsv(path):
|
|
9
|
+
rows = []
|
|
10
|
+
with open(path, encoding='utf-8') as f:
|
|
11
|
+
reader = csv.DictReader(f, delimiter='\t')
|
|
12
|
+
for row in reader:
|
|
13
|
+
rows.append({k: (None if v in ('\\N', 'null', '') else v) for k, v in row.items()})
|
|
14
|
+
return rows
|
|
15
|
+
|
|
16
|
+
def escape_csv(val):
|
|
17
|
+
if val is None: return ''
|
|
18
|
+
s = str(val)
|
|
19
|
+
return f'"{s.replace(chr(34), chr(34)*2)}"' if any(c in s for c in ',"\n') else s
|
|
20
|
+
|
|
21
|
+
HEADERS = ['Type','Name','SKU','Price','Stock','Is Internal','Min Order','Max Order',
|
|
22
|
+
'Image URL','Thumbnail URL','Sequence','Item UID','Print On Receipt',
|
|
23
|
+
'Discountable','Open Editor Panel','Is Delisted','Name Translations','Prices']
|
|
24
|
+
|
|
25
|
+
def make_row(**kw):
|
|
26
|
+
return ','.join(escape_csv(kw.get(k.lower().replace(' ','_').replace('-','_'), '')) for k in HEADERS)
|
|
27
|
+
|
|
28
|
+
def make_row_raw(type='',name='',sku='',price='',stock='',is_internal='',min_order='',max_order='',
|
|
29
|
+
image_url='',thumbnail_url='',sequence='',item_uid='',print_on_receipt='',
|
|
30
|
+
discountable='',open_editor_panel='',is_delisted='',name_translations='',prices=''):
|
|
31
|
+
vals = [type,name,sku,price,stock,is_internal,min_order,max_order,
|
|
32
|
+
image_url,thumbnail_url,sequence,item_uid,print_on_receipt,
|
|
33
|
+
discountable,open_editor_panel,is_delisted,name_translations,prices]
|
|
34
|
+
return ','.join(escape_csv(v) for v in vals)
|
|
35
|
+
|
|
36
|
+
items = [r for r in read_tsv(os.path.join(ROOT,'item.csv')) if r['is_delete'] != '1']
|
|
37
|
+
modifiers = [r for r in read_tsv(os.path.join(ROOT,'modifier.csv')) if r['is_delete'] != '1']
|
|
38
|
+
|
|
39
|
+
# modifier map: item_id -> group_name -> [mods]
|
|
40
|
+
mod_map = defaultdict(lambda: defaultdict(list))
|
|
41
|
+
for m in modifiers:
|
|
42
|
+
mod_map[m['item_id']][m['group_name'] or 'Options'].append(m)
|
|
43
|
+
|
|
44
|
+
# group items by category
|
|
45
|
+
cat_items = defaultdict(list)
|
|
46
|
+
for it in items:
|
|
47
|
+
if it.get('category_id'):
|
|
48
|
+
cat_items[it['category_id']].append(it)
|
|
49
|
+
|
|
50
|
+
# derive category names via keyword heuristics
|
|
51
|
+
KEYWORDS = [
|
|
52
|
+
('Fish', re.compile(r'fish|garoupa|seabass|stingray|fishcake', re.I)),
|
|
53
|
+
('Squid', re.compile(r'squid', re.I)),
|
|
54
|
+
('Chicken', re.compile(r'chicken', re.I)),
|
|
55
|
+
('Prawn', re.compile(r'prawn', re.I)),
|
|
56
|
+
('Beef', re.compile(r'beef', re.I)),
|
|
57
|
+
('Pork', re.compile(r'\bpork\b|satay|skewer', re.I)),
|
|
58
|
+
('Vegetable', re.compile(r'vegetable|veggie|brinjal|broccoli|asparagus|kailan|cabbage|kang kong|petai|bean sprout', re.I)),
|
|
59
|
+
('Soup', re.compile(r'soup|tomyam|tom yam', re.I)),
|
|
60
|
+
('Rice', re.compile(r'\brice\b|fried rice|hor fun', re.I)),
|
|
61
|
+
('Salad', re.compile(r'salad', re.I)),
|
|
62
|
+
('Drinks', re.compile(r'coke|fanta|sprite|drink|coffee|tea|juice|water|beer|wine', re.I)),
|
|
63
|
+
('Omelette', re.compile(r'omelette|omellet', re.I)),
|
|
64
|
+
('Seafood', re.compile(r'seafood', re.I)),
|
|
65
|
+
('Dessert', re.compile(r'cake|ice cream|dessert|pudding|waffle|red ruby|tapioca', re.I)),
|
|
66
|
+
('Set', re.compile(r'^set [a-z]$', re.I)),
|
|
67
|
+
('Extras', re.compile(r'plain rice|spring roll|vermicelli|open item|tofu', re.I)),
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
def infer_name(cid, cat_its):
|
|
71
|
+
counts = defaultdict(int)
|
|
72
|
+
for it in cat_its:
|
|
73
|
+
name = it.get('name') or ''
|
|
74
|
+
for label, rx in KEYWORDS:
|
|
75
|
+
if rx.search(name): counts[label] += 1
|
|
76
|
+
if counts:
|
|
77
|
+
top, cnt = max(counts.items(), key=lambda x: x[1])
|
|
78
|
+
if cnt >= max(1, len(cat_its) * 0.3):
|
|
79
|
+
if top == 'Set':
|
|
80
|
+
# Find the specific set letter from item names
|
|
81
|
+
for it in cat_its:
|
|
82
|
+
m = re.match(r'^set ([a-z])$', (it.get('name') or '').strip(), re.I)
|
|
83
|
+
if m: return f'Set {m.group(1).upper()}'
|
|
84
|
+
return top
|
|
85
|
+
return f'Category {cid}'
|
|
86
|
+
|
|
87
|
+
rows = [','.join(HEADERS)]
|
|
88
|
+
|
|
89
|
+
for cid in sorted(cat_items.keys(), key=lambda x: int(x)):
|
|
90
|
+
cat_its = sorted(cat_items[cid], key=lambda x: int(x.get('sequence') or 0))
|
|
91
|
+
cat_name = infer_name(cid, cat_its)
|
|
92
|
+
rows.append(make_row_raw(type='category', name=cat_name, is_internal='false',
|
|
93
|
+
sequence=cid, is_delisted='false'))
|
|
94
|
+
|
|
95
|
+
for it in cat_its:
|
|
96
|
+
rows.append(make_row_raw(
|
|
97
|
+
type='product', name=it['name'] or '', sku=it.get('codename') or '',
|
|
98
|
+
price=it.get('price') or '0',
|
|
99
|
+
is_internal='true' if it.get('internality') == '1' else 'false',
|
|
100
|
+
sequence=it.get('sequence') or '0',
|
|
101
|
+
image_url=it.get('image_cdn') or '', thumbnail_url=it.get('image_thumbnail_cdn') or '',
|
|
102
|
+
print_on_receipt='true' if it.get('is_print') == '1' else 'false',
|
|
103
|
+
discountable='true' if it.get('discountable') == '1' else 'false',
|
|
104
|
+
is_delisted='false'
|
|
105
|
+
))
|
|
106
|
+
|
|
107
|
+
item_mods = mod_map.get(it['id'])
|
|
108
|
+
if not item_mods: continue
|
|
109
|
+
sorted_groups = sorted(item_mods.items(), key=lambda kv: int(kv[1][0].get('group') or 0))
|
|
110
|
+
for group_name, mods in sorted_groups:
|
|
111
|
+
quota = int(mods[0].get('group_quota') or 0)
|
|
112
|
+
rows.append(make_row_raw(type='modifier_category', name=group_name,
|
|
113
|
+
min_order='0', max_order=str(quota),
|
|
114
|
+
sequence=mods[0].get('group') or '0'))
|
|
115
|
+
for mod in sorted(mods, key=lambda m: int(m['id'])):
|
|
116
|
+
rows.append(make_row_raw(
|
|
117
|
+
type='modifier', name=mod['name'] or '', price=mod.get('price') or '0',
|
|
118
|
+
is_internal='false',
|
|
119
|
+
print_on_receipt='true' if mod.get('is_print') == '1' else 'false'
|
|
120
|
+
))
|
|
121
|
+
|
|
122
|
+
os.makedirs(os.path.dirname(OUT), exist_ok=True)
|
|
123
|
+
with open(OUT, 'w', encoding='utf-8-sig', newline='\n') as f:
|
|
124
|
+
f.write('\n'.join(rows))
|
|
125
|
+
|
|
126
|
+
print(f'Generated {len(rows)-1} rows -> {OUT}')
|
|
127
|
+
print(f'Categories: {len(cat_items)}, Items: {len(items)}, Modifiers: {len(modifiers)}')
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"uid":"inv_iWcYcdv1YNoZC0aGwQxj5","updated_at":"2026-02-14T12:32:28.321Z","created_at":"2026-02-14T12:32:38.731Z","created_at_timestamp":1771072358731,"ref_id":"01260214000001601","call_num":"601","table_uid":"sit_2hPwnc9KSpi39Sn3sFlOi","table_name":"Grab","type":"dine_in","pax":0,"adult_pax":0,"child_pax":0,"baby_pax":0,"subtotal":70000,"point_subtotal":0,"service_chargeable_subtotal":70000,"discountable_subtotal":70000,"service_charge":0,"delivery_id":0,"delivery_charge":0,"delivery_type":"","delivery_by":"","use_customer_point":false,"add_point":0,"minus_point":0,"rewarded_point_rate":0,"rewarded_point":0,"use_customer_balance":false,"add_credit":0,"minus_credit":0,"rewarded_credit_rate":0,"rewarded_credit":0,"discounts":[],"discount_amount":0,"tax":5185.19,"rounding":0,"tip":0,"grand_total":70000,"customer_id":0,"remark":"","sales_user_uid":"","status":"paid","paid_at":"2026-02-14T12:32:38.712Z","voided_at":"1970-01-01T00:00:00.000Z","humanized_paid_at":"14/02/2026 19:32:38","humanized_voided_at":"","humanized_created_at":"14/02/2026 19:32:38","order_id":0,"to_address":{"address1":"","address2":"","lat":"","lng":"","postcode":"","note":"","is_default":false,"name":"","salute":"","phone_number":""},"payment_source_method":"","payment_source_last4":"","payment_source_charge_ref":"","is_sales_exclusive":false,"etc":{},"lines":[{"uid":"iln_qLuhso1UqVWH9ox6JsfGU","updated_at":"2026-02-14T12:32:32.978Z","created_at":"2026-02-14T12:32:32.978Z","created_at_timestamp":1771072352978,"item_uid":"itm_4iQMHedTPbCp3mFokLSvx","price":70000,"unit_of_measure":"quantity","quantity":1,"remark":"","invoice_uid":"","discount_calc_type":"flat","discount_amount":0,"discount_percent":0,"subtotal":70000,"subtotal_before_discount":70000,"item":{"uid":"itm_4iQMHedTPbCp3mFokLSvx","updated_at":"2026-02-13T12:29:18.955Z","created_at":"2025-11-17T07:27:37.134Z","created_at_timestamp":1763364457134,"name":"Grab - H\u1ED3ng Tr\u00E0 S\u1EEFa","name_translations":{"en":"Grab - Black Milk Tea"},"description":"(olong green tea, roasted olong, (avocado,condensed milk,Tapioca white pearl )","description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":170011,"image_url":"https://app.posx.ai/images/93da00e8-5cda-4cbf-be37-0b483b8a99cc.jpg","image_file_path":"","image_thumbnail_url":"https://app.posx.ai/images/93da00e8-5cda-4cbf-be37-0b483b8a99cc.thumbnail.jpg","image_thumbnail_file_path":"","sku":"","type":"by_quantity","barcode":"","category_uid":"cat_nDSPXlpMaHVMyBubKEpO2","price":70000,"original_price":0,"prices":[],"stock":-34,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"codename":"","recipe":"CP003-B01-W01","add_credit":0,"subcategory_uid":"","unit":0,"sold":0,"open_editor":true,"minus_point":0,"cost":0,"discountable":true,"printing_sequence":0,"recommended":false,"likes":0,"dislikes":0,"is_tax_exempt":false,"is_service_charge_exempt":false,"is_point_reward_exempt":false,"is_credit_reward_exempt":false,"is_delisted":false,"kitchen_printers_uids":["prt_UBY9G95mCuHgtg34tnD7H"],"order_printers_uids":[],"label_printers_uids":["prt_B1R1W19J1LRa14nraxiz2"],"open_editor_panel":true,"open_price_editor":true,"combo_groups":[],"id_in_server":9375,"_timestamp":1770985758771696,"modifier_categories":[{"min_order":1,"max_order":1,"sequence":4,"name":"Sugar Level","uid":"imc_5TG1isS3rDcW-TtE9mPvP"},{"min_order":1,"max_order":1,"sequence":3,"name":"Ice","uid":"imc_MA0C7z9B2GHEAplgfMSFc"},{"min_order":1,"max_order":1,"sequence":6,"name":"Topping one","uid":"imc_vQfQFta5UdBpn9KzyItRr"},{"min_order":0,"max_order":0,"sequence":7,"name":"Topping","uid":"imc_UH7jvReaP3k2tyuMUWAm_"}],"modifiers":[{"uid":"iod_JK30rn-ELHCiYyqGy-qi8","updated_at":"2025-11-29T12:05:26.892Z","created_at":"2025-11-29T12:05:26.892Z","created_at_timestamp":1764417926892,"name":"0%","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":4001,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_5TG1isS3rDcW-TtE9mPvP","price":0,"original_price":0,"prices":[],"stock":1000,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":"T05"},{"uid":"iod_2SBZM4Yg0XmqsS3kC9JX1","updated_at":"2025-11-29T12:05:41.197Z","created_at":"2025-11-29T12:05:41.197Z","created_at_timestamp":1764417941197,"name":"30%","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":4002,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_5TG1isS3rDcW-TtE9mPvP","price":0,"original_price":0,"prices":[],"stock":999,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":"T04"},{"uid":"iod_dilkDd6CXmVxDEtoc_cyl","updated_at":"2025-11-29T12:05:51.459Z","created_at":"2025-11-29T12:05:51.459Z","created_at_timestamp":1764417951459,"name":"50%","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":4003,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_5TG1isS3rDcW-TtE9mPvP","price":0,"original_price":0,"prices":[],"stock":994,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":"T03"},{"uid":"iod_wI5ynNWhF2-e2l8wHMYo7","updated_at":"2025-11-29T12:05:59.937Z","created_at":"2025-11-29T12:05:59.937Z","created_at_timestamp":1764417959937,"name":"70%","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":4004,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_5TG1isS3rDcW-TtE9mPvP","price":0,"original_price":0,"prices":[],"stock":999,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":"T02"},{"uid":"iod_UfyyYSUwqyfa5Fek9guGA","updated_at":"2025-11-29T12:06:08.590Z","created_at":"2025-11-29T12:06:08.590Z","created_at_timestamp":1764417968590,"name":"100%","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":4005,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_5TG1isS3rDcW-TtE9mPvP","price":0,"original_price":0,"prices":[],"stock":978,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":"T01"},{"uid":"iod_Wt1BN3gK_ck1HrVeUAbDd","updated_at":"2025-11-30T07:56:42.422Z","created_at":"2025-11-30T07:56:42.422Z","created_at_timestamp":1764489402422,"name":"0%","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":3001,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_MA0C7z9B2GHEAplgfMSFc","price":0,"original_price":0,"prices":[],"stock":1000,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_mMLNAEzOKtY2epTp4gVYN","updated_at":"2025-11-30T07:56:57.428Z","created_at":"2025-11-30T07:56:57.428Z","created_at_timestamp":1764489417428,"name":"50%","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":3002,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_MA0C7z9B2GHEAplgfMSFc","price":0,"original_price":0,"prices":[],"stock":997,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_ZahQa5c3NVufqLt-J_YTw","updated_at":"2025-11-30T07:57:06.578Z","created_at":"2025-11-30T07:57:06.578Z","created_at_timestamp":1764489426578,"name":"70%","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":3003,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_MA0C7z9B2GHEAplgfMSFc","price":0,"original_price":0,"prices":[],"stock":998,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_gJcG8TTG20hD9BQLWCQGZ","updated_at":"2025-11-30T07:57:15.527Z","created_at":"2025-11-30T07:57:15.527Z","created_at_timestamp":1764489435527,"name":"100%","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":3004,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_MA0C7z9B2GHEAplgfMSFc","price":0,"original_price":0,"prices":[],"stock":975,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_gj9FzI7-W64UmxsFr5IBr","updated_at":"2025-12-01T02:43:49.893Z","created_at":"2025-12-01T02:43:49.893Z","created_at_timestamp":1764557029893,"name":"Kem Mu\u1ED1i","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":6001,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_vQfQFta5UdBpn9KzyItRr","price":0,"original_price":0,"prices":[],"stock":1000,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_qJbUtYkEmbxWCVP05oOpa","updated_at":"2025-12-01T02:44:36.158Z","created_at":"2025-12-01T02:44:36.158Z","created_at_timestamp":1764557076158,"name":"Kem Tiramisu","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":6002,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_vQfQFta5UdBpn9KzyItRr","price":0,"original_price":0,"prices":[],"stock":998,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_NC_3a3Jx4Ss67wgQ7gxku","updated_at":"2025-12-01T02:44:43.988Z","created_at":"2025-12-01T02:44:43.988Z","created_at_timestamp":1764557083988,"name":"Kem Matcha","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":6003,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_vQfQFta5UdBpn9KzyItRr","price":0,"original_price":0,"prices":[],"stock":998,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_ys_x93jxoF45IxkQdn1V5","updated_at":"2025-12-01T02:44:52.823Z","created_at":"2025-12-01T02:44:52.823Z","created_at_timestamp":1764557092823,"name":"Kem Khoai M\u00F4n","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":6004,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_vQfQFta5UdBpn9KzyItRr","price":0,"original_price":0,"prices":[],"stock":1000,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_X8sPo6TuUmiyWKUMIt2En","updated_at":"2025-12-01T02:39:12.571Z","created_at":"2025-12-01T02:39:12.571Z","created_at_timestamp":1764556752571,"name":"H\u1EA1t Sen","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":7001,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_UH7jvReaP3k2tyuMUWAm_","price":20000,"original_price":0,"prices":[],"stock":1000,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_NP_ELMackBiRKt4zUJuYm","updated_at":"2025-12-01T02:39:22.344Z","created_at":"2025-12-01T02:39:22.344Z","created_at_timestamp":1764556762344,"name":"Tr\u00E2n Ch\u00E2u Tr\u1EAFng","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":7002,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_UH7jvReaP3k2tyuMUWAm_","price":10000,"original_price":0,"prices":[],"stock":1000,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_Pv_E-aBfwnFUDobeVZQrj","updated_at":"2025-12-01T02:39:29.101Z","created_at":"2025-12-01T02:39:29.101Z","created_at_timestamp":1764556769101,"name":"Tr\u00E2n Ch\u00E2u \u0110\u01B0\u1EDDng \u0110en","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":7003,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_UH7jvReaP3k2tyuMUWAm_","price":10000,"original_price":0,"prices":[],"stock":1000,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_srhIckzDJzrX_ngNlzWEi","updated_at":"2025-12-01T02:39:38.616Z","created_at":"2025-12-01T02:39:38.616Z","created_at_timestamp":1764556778617,"name":"Th\u1EA1ch Chanh V\u00E0ng","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":7004,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_UH7jvReaP3k2tyuMUWAm_","price":10000,"original_price":0,"prices":[],"stock":999,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_tAxh_GII-A9nxPYQQ7pXH","updated_at":"2025-12-01T02:39:46.145Z","created_at":"2025-12-01T02:39:46.145Z","created_at_timestamp":1764556786145,"name":"Th\u1EA1ch Nha \u0110am","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":7005,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_UH7jvReaP3k2tyuMUWAm_","price":10000,"original_price":0,"prices":[],"stock":1000,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_DfzW0yeeEDGjfLPDyMDP9","updated_at":"2025-12-01T02:39:52.931Z","created_at":"2025-12-01T02:39:52.931Z","created_at_timestamp":1764556792931,"name":"B\u00E1nh L\u1ECDt","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":7006,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_UH7jvReaP3k2tyuMUWAm_","price":10000,"original_price":0,"prices":[],"stock":1000,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_dSHn36b5-UNatGPAXTA7-","updated_at":"2025-12-01T02:40:01.345Z","created_at":"2025-12-01T02:40:01.345Z","created_at_timestamp":1764556801345,"name":"Kh\u00FAc B\u1EA1ch","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":7007,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_UH7jvReaP3k2tyuMUWAm_","price":15000,"original_price":0,"prices":[],"stock":999,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_JwZio9Uv3BpOwQeDUgJt1","updated_at":"2025-12-01T02:40:11.646Z","created_at":"2025-12-01T02:40:11.646Z","created_at_timestamp":1764556811646,"name":"Nh\u00E3n Ng\u00E2m","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":7008,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_UH7jvReaP3k2tyuMUWAm_","price":20000,"original_price":0,"prices":[],"stock":1000,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},{"uid":"iod_mmjGhQEIMoIT_v2Aopqre","updated_at":"2025-12-01T02:40:22.943Z","created_at":"2025-12-01T02:40:22.943Z","created_at_timestamp":1764556822943,"name":"\u0110\u00E0o Mi\u1EBFng","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":7009,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_UH7jvReaP3k2tyuMUWAm_","price":20000,"original_price":0,"prices":[],"stock":1000,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""}],"print_template_uid":""},"point":0,"point_subtotal":0,"up_size":false,"is_take_out":false,"is_duplicate":false,"is_combo":false,"is_combo_item":false,"combo_group_uid":"","combo_line_uid":"","modifiers":[{"item_uid":"iod_gJcG8TTG20hD9BQLWCQGZ","item":{"uid":"iod_gJcG8TTG20hD9BQLWCQGZ","updated_at":"2025-11-30T07:57:15.527Z","created_at":"2025-11-30T07:57:15.527Z","created_at_timestamp":1764489435527,"name":"100%","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":3004,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_MA0C7z9B2GHEAplgfMSFc","price":0,"original_price":0,"prices":[],"stock":975,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":""},"price":0,"unit_of_measure":"quantity","quantity":1,"sent":0,"is_cancel_printed":false,"selected":false,"sales_user_uid":""},{"item_uid":"iod_UfyyYSUwqyfa5Fek9guGA","item":{"uid":"iod_UfyyYSUwqyfa5Fek9guGA","updated_at":"2025-11-29T12:06:08.590Z","created_at":"2025-11-29T12:06:08.590Z","created_at_timestamp":1764417968590,"name":"100%","name_translations":{},"description_translations":{},"meta":{},"etc":{},"assets":{},"config":{"online_order_display_large_image":false},"is_internal":false,"color":"","icon":"","sequence":4005,"image_url":"","image_file_path":"","image_thumbnail_url":"","image_thumbnail_file_path":"","visibility":[],"sku":"","type":"modifier","barcode":"","category_uid":"imc_5TG1isS3rDcW-TtE9mPvP","price":0,"original_price":0,"prices":[],"stock":978,"print_on_receipt":true,"price_adjustable":false,"is_sold_out":false,"recipe":"T01"},"price":0,"unit_of_measure":"quantity","quantity":1,"sent":0,"is_cancel_printed":false,"selected":false,"sales_user_uid":""}],"duplicated_from_uid":"","sent":1,"is_cancel_printed":false,"selected":false,"sales_user_uid":"","meta":{}}],"deleted_lines":[],"payment_has_cash":false,"payments":[{"uid":"pay_rqbYCVJ0tM9YqEtBBGGFg","updated_at":"2026-02-14T12:32:38.646Z","created_at":"2026-02-14T12:32:38.646Z","created_at_timestamp":1771072358646,"invoice_uid":"inv_iWcYcdv1YNoZC0aGwQxj5","tender_amount":70000,"change_amount":0,"payment_method_uid":"pmd_w5xPnDgpTeU2qufEmt9qz","payment_method_code":"ZALO","payment_method_name":"Zalo Pay","payment_method":{"uid":"pmd_w5xPnDgpTeU2qufEmt9qz","updated_at":"2026-01-24T15:20:40.450Z","created_at":"2025-11-25T06:02:37.244Z","created_at_timestamp":1764050557244,"deleted_at":null,"name":"Zalo Pay","name_translations":{},"sequence":0,"payment_currency_id":0,"currency_code":"","currency_symbol":"$","payment_method_code":"ZALO","codename":"","name_abbrev":"","enable_drawer":false,"enable_receipt":true,"image_url":"https://revopos-cdn.sgp1.digitaloceanspaces.com/4d49a465-47bb-44ca-8cdc-59f58c40e0cf.png","is_cash":false,"is_integrated":true,"is_disabled":false,"type":0,"api_id":"","api_key":"","is_sales_exclusive":false,"id_in_server":6742,"_timestamp":1768964152742600,"custom_image_url":"","enable_credit_validation":false,"enable_customer_validation":false,"is_cancellable":false},"payment_method_history":{"uid":"pmd_w5xPnDgpTeU2qufEmt9qz","updated_at":"2026-01-24T15:20:40.450Z","created_at":"2025-11-25T06:02:37.244Z","created_at_timestamp":1764050557244,"deleted_at":null,"name":"Zalo Pay","name_translations":{},"sequence":0,"payment_currency_id":0,"currency_code":"","currency_symbol":"$","payment_method_code":"ZALO","codename":"","name_abbrev":"","enable_drawer":false,"enable_receipt":true,"image_url":"https://revopos-cdn.sgp1.digitaloceanspaces.com/4d49a465-47bb-44ca-8cdc-59f58c40e0cf.png","is_cash":false,"is_integrated":true,"is_disabled":false,"type":0,"api_id":"","api_key":"","is_sales_exclusive":false,"id_in_server":6742,"_timestamp":1768964152742600,"custom_image_url":"","enable_credit_validation":false,"enable_customer_validation":false,"is_cancellable":false,"changed_at":"2026-02-14T14:34:38.555Z"}}],"table_switch_id":0,"employee_uid":"emp_ux2a3RLr1KZDYuHz_B7AP","employee_name":"Hamster","receipt_print_override":false,"customer":{"id":0,"ref_id":"","image_url":"","stripe_token":"","stripe_customer_id":"","username":"","normalized_username":"","company":"","ic":"","display_name":"","full_name":"","birth_date":"","gender":"","address":"","postcode":"","note":"","language":"","time_zone":"","created_at":"2026-02-14T12:32:28.322Z","updated_at":"2026-02-14T12:32:28.322Z","license_ids":"","security_stamp":"","phone_number":"","license_id":0,"credit":0,"point":0,"transaction_total":0,"transaction_time":0,"user_group_id":0,"user_group_name":"","error":""},"is_duplicate":false,"kitchen_print_override":false,"otp":"","activities":[],"receipt_print_job_uid":"pjb_6AY0c1t9DYoqZfiglWruH","kitchen_print_job_uids":[["pjb_6lIIH_bLvO2QJJ701v4Hx"]],"label_print_job_uids":[["pjb_TEy4QwnyPiP_ryRMpMNNB"]],"charges":[],"is_charge_triggered":false,"meta":{"total_item_quantity":1},"price_lookup_key":"","id_in_server":252475,"_timestamp":1771079680743354}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1B401B45001B34001B2D000A1B45011B61011D2111204861707079205468616920466F6F64202D20566963746F727920380A1D21001B45001B34001B2D000A1B45011B610138204A616C616E204C6567756E6469202330312D313020566963746F727920380A1B45001B34001B2D000A1B45011B610154656C3A2036373134363638320A1B45001B34001B2D000A1B45011B610044696E652D496E0A1B45001B34001B2D000A1B45011B6100232030313236303231303030303030313437320A1B45001B34001B2D000A1B45011B61001D21115461626C653A31350A1D21001B45001B34001B2D000A1B45011B61001D21114F52444552204E4F2E3A203437320A1D21001B45001B34001B2D000A1B45011B61005365727665642042793A61646D696E0A1B45001B34001B2D000A1B45011B6100446174653A323032362D4665622D31302031333A34313A35340A1B45001B34001B2D000A1B45001B34001B2D002D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D0A1B45014954454D20202020202020202020202020202020202020202020202051545920202020414D542020202020202020200A1B45001B34001B2D002D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D0A1B45011B61004D332E204761726C696320467269656420506F726B20202020202020783120202020202431332E39302020202020200A1B45011B61005445352E20436869636B656E204F6D656C65747465202020202020207831202020202024372E3930202020202020200A1B45011B61004E44322E20506861642073656520457720284B7761792054202020207831202020202024392E3930202020202020200A656F772920536561666F6F6420202020202020202020202020202020202020202020202020202020202020202020200A1B45011B610041342E205468616920507261776E2043616B65202834205020202020783120202020202431332E39302020202020200A69656365732920202020202020202020202020202020202020202020202020202020202020202020202020202020200A1B45011B6100445231312E2054686169204D696C6B205465612020202020202020207831202020202024342E3530202020202020200A1B45011B61004452372E204C656D6F6E6772617373204472696E6B202020202020207831202020202024332E3930202020202020200A1B45001B34001B2D001B61002D2020436F6C642020202020202020202020202020202020202020207B7B7D7D2020202020202020202020202020200A1B45001B34001B2D000A1B45001B34001B2D002D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D0A1B45011B6100535542544F54414C202020202020202020202020202020202020202020202020202020202435342E30302020202020200A1B450131302520534552564943452043484152474520202020202020202020202020202020202024352E3430202020202020200A1B4501544F54414C202020202020202020202020202020202020202020202020202020202020202435392E34302020202020200A1B45001B34001B2D002D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D0A1B45015041594D454E542042593A466975752020202020202020202020202020202020202020202435392E34302020202020200A1B45001B34001B2D002D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D0A1B45015041594D454E5420545950452020202020202020202020204D41535445524341524420202020202020202020202020200A1B45015245462020202020202020202020202020202020202020203334383732363039343320202020202020202020202020200A1B450143415244204E554D202020202020202020202020202020202A2A2A2A202A2A2A2A202A2A2A2A203130343020202020200A1B4501435553544F4D45522054454C2020202020202020202020202020202020202020202020202B36352A2A2A2A34383436200A1B450143617368204261636B20202020202020202020202020202020202020202020202020202024312E3738202020202020200A1B45014372656469742042616C616E63652020202020202020202020202020202020202020202024312E3738202020202020200A1B4501506F696E742042616C616E636520202020202020202020202020202020202020202020207074202020202020202020200A1B45001B34001B2D002D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D0A1B45011B610138204A616C616E204C6567756E6469202330312D313020566963746F727920380A1B45001B34001B2D000A0A0A0A0A1B45001B34001B2D000A1B45001B34001B2D000A1B45001B34001B2D000A1B45001B34001B2D001D5600
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"uid": "pjb_I8RJcR0Hm6iMsYSzmIDRd", "updated_at": "2026-02-10T05:41:54.209Z", "created_at": "2026-02-10T05:41:54.356Z", "created_at_timestamp": 1770702114356, "deleted_at": null, "name_translations": {}, "printer_name": "Receipt Printer", "driver_printer_name": "Receipt Printer", "printer_ip": "0.0.0.0", "printer_uid": "prt_eJ5yITaByzBnAXSz1IvhF", "printer_type": "receipt_printer", "protocol": "ESC/POS", "connection_type": "WINDOWS_DRIVER", "data": {"codepage": "cp936", "items": [{"type": "newline", "repeat": 1}, {"type": "text", "align": "center", "style": "bold", "item": {"size": "custom", "width": 1, "height": 1, "text": " Happy Thai Food - Victory 8"}}, {"type": "newline", "repeat": 1}, {"type": "text", "align": "center", "style": "bold", "item": {"text": "8 Jalan Legundi #01-10 Victory 8"}}, {"type": "newline", "repeat": 1}, {"type": "text", "align": "center", "style": "bold", "item": {"text": "Tel: 67146682"}}, {"type": "newline", "repeat": 1}, {"type": "text", "align": "left", "style": "bold", "item": {"text": "Dine-In"}}, {"type": "newline", "repeat": 1}, {"type": "text", "align": "left", "style": "bold", "item": {"text": "# 01260210000001472"}}, {"type": "newline", "repeat": 1}, {"type": "text", "align": "left", "style": "bold", "item": {"size": "custom", "width": 1, "height": 1, "text": "Table:15"}}, {"type": "newline", "repeat": 1}, {"type": "text", "align": "left", "style": "bold", "item": {"size": "custom", "width": 1, "height": 1, "text": "ORDER NO.: 472"}}, {"type": "newline", "repeat": 1}, {"type": "text", "align": "left", "style": "bold", "item": {"text": "Served By:admin"}}, {"type": "newline", "repeat": 1}, {"type": "text", "align": "left", "style": "bold", "item": {"text": "Date:2026-Feb-10 13:41:54"}}, {"type": "newline", "repeat": 1}, {"type": "line"}, {"type": "table", "style": "bold", "table": {"items": [{"text": "ITEM", "column_width": 50}, {"text": "", "column_width": 10}, {"text": "QTY", "column_width": 15}, {"text": "AMT", "column_width": 25}]}}, {"type": "line"}, {"type": "table", "align": "left", "style": "bold", "table": {"items": [{"text": "M3. Garlic Fried Pork ", "column_width": 50}, {"text": "", "column_width": 10}, {"text": "x1", "column_width": 15}, {"text": "$13.90", "column_width": 25}]}}, {"type": "table", "align": "left", "style": "bold", "table": {"items": [{"text": "TE5. Chicken Omelette ", "column_width": 50}, {"text": "", "column_width": 10}, {"text": "x1", "column_width": 15}, {"text": "$7.90", "column_width": 25}]}}, {"type": "table", "align": "left", "style": "bold", "table": {"items": [{"text": "ND2. Phad see Ew (Kway Teow) Seafood ", "column_width": 50}, {"text": "", "column_width": 10}, {"text": "x1", "column_width": 15}, {"text": "$9.90", "column_width": 25}]}}, {"type": "table", "align": "left", "style": "bold", "table": {"items": [{"text": "A4. Thai Prawn Cake (4 Pieces) ", "column_width": 50}, {"text": "", "column_width": 10}, {"text": "x1", "column_width": 15}, {"text": "$13.90", "column_width": 25}]}}, {"type": "table", "align": "left", "style": "bold", "table": {"items": [{"text": "DR11. Thai Milk Tea ", "column_width": 50}, {"text": "", "column_width": 10}, {"text": "x1", "column_width": 15}, {"text": "$4.50", "column_width": 25}]}}, {"type": "table", "align": "left", "style": "bold", "table": {"items": [{"text": "DR7. Lemongrass Drink ", "column_width": 50}, {"text": "", "column_width": 10}, {"text": "x1", "column_width": 15}, {"text": "$3.90", "column_width": 25}]}}, {"type": "table", "align": "left", "table": {"items": [{"text": "- Cold ", "column_width": 50}, {"text": "", "column_width": 10}, {"text": "{{}}", "column_width": 15}, {"text": "", "column_width": 25}]}}, {"type": "newline", "repeat": 1}, {"type": "line"}, {"codepage": "cp437", "type": "table", "align": "left", "style": "bold", "table": {"items": [{"text": "SUBTOTAL", "column_width": 50}, {"text": "", "column_width": 25}, {"text": "$54.00", "column_width": 25}]}}, {"codepage": "cp437", "type": "table", "style": "bold", "table": {"items": [{"text": "10% SERVICE CHARGE", "column_width": 50}, {"text": "", "column_width": 25}, {"text": "$5.40", "column_width": 25}]}}, {"codepage": "cp437", "type": "table", "style": "bold", "size": "custom", "width": 1, "height": 1, "table": {"items": [{"text": "TOTAL", "column_width": 50}, {"text": "", "column_width": 25}, {"text": "$59.40", "column_width": 25}]}}, {"type": "line"}, {"codepage": "cp437", "type": "table", "style": "bold", "table": {"items": [{"text": "PAYMENT BY:Fiuu", "column_width": 50}, {"text": "", "column_width": 25}, {"text": "$59.40", "column_width": 25}]}}, {"type": "line"}, {"codepage": "cp437", "type": "table", "style": "bold", "table": {"items": [{"text": "PAYMENT TYPE", "column_width": 50}, {"text": "MASTERCARD", "column_width": 50}]}}, {"codepage": "cp437", "type": "table", "style": "bold", "table": {"items": [{"text": "REF", "column_width": 50}, {"text": "3487260943", "column_width": 50}]}}, {"codepage": "cp437", "type": "table", "style": "bold", "table": {"items": [{"text": "CARD NUM", "column_width": 50}, {"text": "**** **** **** 1040", "column_width": 50}]}}, {"type": "table", "style": "bold", "table": {"items": [{"text": "CUSTOMER TEL", "column_width": 50}, {"text": "", "column_width": 25}, {"text": "\u002B65****4846", "column_width": 25}]}}, {"type": "table", "style": "bold", "table": {"items": [{"text": "Cash Back", "column_width": 50}, {"text": "", "column_width": 25}, {"text": "$1.78", "column_width": 25}]}}, {"type": "table", "style": "bold", "table": {"items": [{"text": "Credit Balance", "column_width": 50}, {"text": "", "column_width": 25}, {"text": "$1.78", "column_width": 25}]}}, {"type": "table", "style": "bold", "table": {"items": [{"text": "Point Balance", "column_width": 50}, {"text": "", "column_width": 25}, {"text": "pt", "column_width": 25}]}}, {"type": "line"}, {"type": "text", "align": "center", "style": "bold", "item": {"text": "8 Jalan Legundi #01-10 Victory 8"}}, {"type": "newline", "repeat": 5}, {"type": "newline", "repeat": 1}, {"type": "newline", "repeat": 1}, {"type": "newline", "repeat": 1}, {"type": "cut"}]}, "status": "printed", "retries": 0, "commands": "", "save_only": false, "print_times": 1, "printed": 1, "device_uid_to_print": "", "designated_table_section_uids": [], "job_id": 0, "print_sequence": 0, "meta": {}, "id_in_server": 1044494, "_timestamp": 1770702115370025}
|