1+ var pur = {
2+ // (A) SHOW ALL PURCHASES
3+ pg : 1 , // current page
4+ find : "" , // current search
5+ list : silent => {
6+ if ( silent !== true ) { cb . page ( 1 ) ; }
7+ cb . load ( {
8+ page : "purchase/list" , target : "pur-list" ,
9+ data : {
10+ page : pur . pg ,
11+ search : pur . find
12+ }
13+ } ) ;
14+ } ,
15+
16+ // (B) GO TO PAGE
17+ // pg : page number
18+ goToPage : pg => { if ( pg != pur . pg ) {
19+ pur . pg = pg ;
20+ pur . list ( ) ;
21+ } } ,
22+
23+ // (C) SEARCH PURCHASES
24+ search : ( ) => {
25+ pur . find = document . getElementById ( "pur-search" ) . value ;
26+ pur . pg = 1 ;
27+ pur . list ( ) ;
28+ return false ;
29+ } ,
30+
31+ // (D) SHOW ADD/EDIT DOCKET
32+ // id : purchase id, for edit only
33+ iList : null , // current items for the order
34+ addEdit : id => cb . load ( {
35+ page : "purchase/form" , target : "cb-page-2" ,
36+ data : { id : id ? id : "" } ,
37+ onload : ( ) => {
38+ // (D1) RESET ITEMS LIST
39+ pur . iList = { } ;
40+
41+ // (D2) DRAW ITEMS
42+ if ( id ) {
43+ let hitems = document . getElementById ( "pur-items-data" ) ,
44+ items = JSON . parse ( hitems . innerHTML ) ;
45+ hitems . remove ( ) ;
46+ items . forEach ( i => pur . addItem ( ...i ) ) ;
47+ }
48+
49+ // (D3) ATTACH SUPPLIER AUTOCOMPLETE
50+ var hsupname = document . getElementById ( "sup-name" ) ;
51+ if ( ! hsupname . disabled ) {
52+ autocomplete . attach ( {
53+ target : hsupname ,
54+ mod : "autocomplete" , act : "sup" ,
55+ data : { more : 1 } ,
56+ onpick : sup => {
57+ hsupname . value = sup . n ;
58+ sup . v = JSON . parse ( sup . v ) ;
59+ document . getElementById ( "sup-id" ) . value = sup . v . i ;
60+ pur . csup ( false ) ;
61+ }
62+ } ) ;
63+ }
64+
65+ // (D4) ATTACH ADD ITEM AUTOCOMPLETE
66+ autocomplete . attach ( {
67+ target : document . getElementById ( "add-item" ) ,
68+ mod : "autocomplete" , act : "supitem" ,
69+ data : {
70+ more : 1 ,
71+ sid : document . getElementById ( "sup-id" )
72+ } ,
73+ onpick : item => {
74+ document . getElementById ( "add-item" ) . value = "" ;
75+ item = JSON . parse ( item . v ) ;
76+ pur . addItem ( item . s , item . ss , item . n , item . u , item . p , 1 ) ;
77+ }
78+ } ) ;
79+
80+ // (D5) ATTACH NFC ADD ITEM
81+ if ( "NDEFReader" in window ) {
82+ nfc . init ( pur . addGet ) ;
83+ if ( id ) { document . getElementById ( "nfc-btn" ) . disabled = false ; }
84+ }
85+
86+ // (D6) SHOW PURCHASE ORDER PAGE
87+ cb . page ( 2 ) ;
88+ }
89+ } ) ,
90+
91+ // (E) TOGGLE SUPPLIER CHANGE
92+ csup : reset => {
93+ // (E1) GET HTML ELEMENTS
94+ let n = document . getElementById ( "sup-name" ) ,
95+ i = document . getElementById ( "sup-id" ) ,
96+ c = document . getElementById ( "sup-change" ) ,
97+ it = document . getElementById ( "pur-items" ) ,
98+ ait = document . getElementById ( "add-item" ) ,
99+ aqr = document . getElementById ( "qr-btn" ) ,
100+ anfc = document . getElementById ( "nfc-btn" ) ;
101+
102+ // (E2) RESET SUPPLIER
103+ if ( reset ) {
104+ n . value = "" ;
105+ n . disabled = false ;
106+ i . value = "" ;
107+ c . classList . add ( "d-none" ) ;
108+ pur . iList = { } ;
109+ it . innerHTML = "" ;
110+ ait . disabled = true ;
111+ aqr . disabled = true ;
112+ if ( "NDEFReader" in window ) { anfc . disabled = true ; }
113+ }
114+
115+ // (E3) SET SUPPLIER
116+ else {
117+ n . disabled = true ;
118+ c . classList . remove ( "d-none" ) ;
119+ ait . disabled = false ;
120+ aqr . disabled = false ;
121+ if ( "NDEFReader" in window ) { anfc . disabled = false ; }
122+ }
123+ } ,
124+
125+ // (F) ADD ITEM ROW
126+ addItem : ( sku , ssku , name , unit , price , qty ) => {
127+ // (F1) CHECK DUPLICATE ITEM
128+ if ( pur . iList [ sku ] ) {
129+ cb . modal ( "Already Added" , `[${ sku } ] ${ name } is already added.` ) ;
130+ }
131+
132+ // (F2) ADD NEW ROW
133+ else {
134+ // (F2-1) ITEM ROW HTML
135+ let row = document . createElement ( "div" ) ;
136+ row . className = "iRow d-flex align-items-center border p-2" ;
137+ row . innerHTML =
138+ `<i class="text-danger icon-cross p-3" onclick="pur.delItem(this, '${ sku } ')"></i>
139+ <div class="flex-grow-1">
140+ <strong class="iSKU">${ sku } </strong>
141+ <div class="iName">${ name } </div>
142+ </div>
143+ <div class="form-floating">
144+ <input class="form-control mx-1 iQty" type="number" step="0.01" min="0.01" required value="${ qty } ">
145+ <label class="iUnit">${ unit } </label>
146+ </div>
147+ <div class="form-floating">
148+ <input class="form-control iPrice" type="number" step="0.01" min="0" required value="${ price } ">
149+ <label>PRICE</label>
150+ </div>` ;
151+
152+ // (F2-2) SORTABLE
153+ row . draggable = true ;
154+ row . ondragstart = ( ) => pur . ddfrom = row ;
155+ row . ondragover = e => e . preventDefault ( ) ;
156+ row . ondrop = pur . isort ;
157+
158+ // (F2-3) APPEND TO LIST
159+ document . getElementById ( "pur-items" ) . appendChild ( row ) ;
160+ pur . iList [ sku ] = 1 ;
161+ }
162+ } ,
163+
164+ // (G) DRAG-N-DROP SORT ITEM
165+ ddfrom : null , // current item being dragged
166+ ddto : null , // dropped at this item
167+ ddget : r => r . classList . contains ( "iRow" ) ? r : pur . ddget ( r . parentElement ) , // get proper drop target
168+ isort : e => {
169+ // (G1) GET ELEMENTS
170+ e . preventDefault ( ) ;
171+ let iList = document . getElementById ( "pur-items" ) ,
172+ iAll = iList . querySelectorAll ( ".iRow" ) ;
173+ pur . ddto = pur . ddget ( e . target ) ;
174+
175+ // (G2) REORDER ITEM
176+ if ( iAll . length > 1 && pur . ddfrom != pur . ddto ) {
177+ let currentpos = 0 , droppedpos = 0 ;
178+ for ( let i = 0 ; i < iAll . length ; i ++ ) {
179+ if ( pur . ddfrom == iAll [ i ] ) { currentpos = i ; }
180+ if ( pur . ddto == iAll [ i ] ) { droppedpos = i ; }
181+ }
182+ if ( currentpos < droppedpos ) {
183+ iList . insertBefore ( pur . ddfrom , pur . ddto . nextSibling ) ;
184+ } else {
185+ iList . insertBefore ( pur . ddfrom , pur . ddto ) ;
186+ }
187+ }
188+ } ,
189+
190+ // (H) REMOVE ITEM ROW
191+ delItem : ( row , sku ) => {
192+ row . parentElement . remove ( ) ;
193+ delete pur . iList [ sku ] ;
194+ } ,
195+
196+ // (I) GET ITEM FROM SERVER & ADD TO LIST
197+ addGet : sku => cb . api ( {
198+ mod : "suppliers" , act : "getItem" ,
199+ data : {
200+ id : document . getElementById ( "sup-id" ) . value ,
201+ sku : sku
202+ } ,
203+ passmsg : false ,
204+ onpass : res => {
205+ // (I1) INVALID SKU
206+ if ( res . data == null ) {
207+ cb . modal ( "Invalid Item" , `${ sku } is not found in the database, or supplier does not have this item.` ) ;
208+ }
209+
210+ // (I2) OK - ADD ITEM
211+ else {
212+ let i = res . data ;
213+ pur . addItem ( i . item_sku , i . sup_sku , i . item_name , i . item_unit , i . unit_price , 1 ) ;
214+ }
215+ }
216+ } ) ,
217+
218+ // (J) ADD ITEM WITH QR CODE
219+ addQR : ( ) => {
220+ if ( qrscan . scanner == null ) { qrscan . init ( pur . addGet ) ; }
221+ qrscan . show ( ) ;
222+ } ,
223+
224+ // (K) SAVE PURCHASE
225+ save : ( ) => {
226+ // (K1) GET DATA
227+ var data = {
228+ sid : document . getElementById ( "sup-id" ) . value ,
229+ name : document . getElementById ( "p-name" ) . value ,
230+ tel : document . getElementById ( "p-tel" ) . value ,
231+ email : document . getElementById ( "p-email" ) . value ,
232+ address : document . getElementById ( "p-address" ) . value ,
233+ date : document . getElementById ( "p-date" ) . value ,
234+ notes : document . getElementById ( "p-notes" ) . value
235+ } ;
236+ if ( data . sid == "" ) {
237+ cb . modal ( "No supplier specified" , "Please select a supplier." ) ;
238+ return false ;
239+ }
240+ var id = document . getElementById ( "p-id" ) . value ;
241+ if ( id != "" ) {
242+ data . id = id ;
243+ data . stat = document . getElementById ( "p-stat" ) . value ;
244+ }
245+
246+ // (K2) GET ITEMS
247+ let items = document . querySelectorAll ( "#pur-items .iRow" ) ;
248+ if ( items . length == 0 ) {
249+ cb . modal ( "No Items" , "Please add at least one item." ) ;
250+ return false ;
251+ }
252+ data . items = [ ] ;
253+ // sku | price | qty
254+ for ( let i of items ) {
255+ data . items . push ( [
256+ i . querySelector ( ".iSKU" ) . innerHTML ,
257+ i . querySelector ( ".iPrice" ) . value ,
258+ i . querySelector ( ".iQty" ) . value
259+ ] ) ;
260+ }
261+ data . items = JSON . stringify ( data . items ) ;
262+
263+ // (K3) AJAX
264+ cb . api ( {
265+ mod : "purchase" , act : "save" ,
266+ data : data ,
267+ passmsg : "Order saved" ,
268+ onpass : pur . list
269+ } ) ;
270+ return false ;
271+ } ,
272+
273+ // (L) PRINT PURCHASE ORDER
274+ print : id => {
275+ document . getElementById ( "pur-print-id" ) . value = id ;
276+ document . getElementById ( "pur-print" ) . submit ( ) ;
277+ }
278+ } ;
279+
280+ // (M) INIT MANAGE PURCHASES
281+ window . addEventListener ( "load" , ( ) => {
282+ // (M1) EXTRA STYLES FOR "ADD/EDIT ITEMS LIST"
283+ document . head . appendChild ( document . createElement ( "style" ) ) . innerHTML = ".iQty,.iPrice{width:80px}" ;
284+
285+ // (M2) LIST PURCHASES
286+ pur . list ( ) ;
287+
288+ // (M3) ATTACH AUTOCOMPLETE
289+ autocomplete . attach ( {
290+ target : document . getElementById ( "pur-search" ) ,
291+ mod : "autocomplete" , act : "purchase" ,
292+ onpick : res => pur . search ( )
293+ } ) ;
294+ } ) ;
0 commit comments