File size: 16,915 Bytes
94211e0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
/**
 * V.AI STUDIO — Comprehensive Fix for Brand Discounts, Excel Formulas & Notes
 * ========================================================================
 * Fixes:
 * 1. Brand-level discount parsing: "Malloca Ck% 35%", "eurogold ck% 50%" → itemDiscounts
 * 2. Excel discount column (Giá CK) uses FORMULA (G*0.65) not hardcoded value
 * 3. Notes from AI prompt are filtered out from Excel/modal (no "Ghi chú:" row)
 * 4. Fees (giao hàng, lắp đặt, cọc, bốc xếp) appear as separate lines in Excel
 * 5. Applies to both _doExportExcel and _doExportDeliveryExcel
 */

(function() {
  'use strict';
  if (window.__COMPREHENSIVE_FIX__) return;
  window.__COMPREHENSIVE_FIX__ = true;

  // Wait for VAIR_QR to be available
  function waitForVAIQR() {
    return new Promise(resolve => {
      if (window.VAI_QR) {
        resolve(window.VAI_QR);
        return;
      }
      let attempts = 0;
      const check = setInterval(() => {
        attempts++;
        if (window.VAI_QR) {
          clearInterval(check);
          resolve(window.VAI_QR);
        } else if (attempts > 100) { // 10 seconds
          clearInterval(check);
          console.warn('[Comprehensive Fix] VAI_QR not found after 10s');
          resolve(null);
        }
      }, 100);
    });
  }

  waitForVAIQR().then(VAI_QR => {
    if (!VAI_QR) return;

    // =====================================================================
    // 1. PATCH parsePrompt TO HANDLE BRAND-LEVEL DISCOUNTS
    // =====================================================================
    if (VAI_QR.parsePrompt && !VAI_QR.parsePrompt._patchedBrandDiscount) {
      const origParsePrompt = VAI_QR.parsePrompt;
      
      VAI_QR.parsePrompt = function(text) {
        // First, extract brand-level discounts BEFORE the original parser runs
        // Pattern: "BrandName Ck% XX%" or "BrandName ck% XX" or "BrandName chiết khấu XX%"
        const brandDiscounts = {};
        const brandDiscountRegex = /\b([a-zA-Z][a-zA-Z0-9\s\-]{1,30}?)\s+(?:ck|chiết\s*khấu|chiet\s*khau|giảm|giam|discount)\s*([\d.,]+)\s*%/gi;
        let match;
        while ((match = brandDiscountRegex.exec(text)) !== null) {
          const brand = match[1].trim().toLowerCase();
          const pct = parseFloat(match[2].replace(/\./g, '').replace(',', '.'));
          if (pct > 0 && pct <= 100) {
            brandDiscounts[brand] = pct;
          }
        }
        
        // Remove brand discount lines from text so they don't get parsed as fees
        let cleanText = text;
        if (Object.keys(brandDiscounts).length > 0) {
          cleanText = text.replace(brandDiscountRegex, '');
        }
        
        // Call original parser with cleaned text
        const result = origParsePrompt.call(this, cleanText);
        
        // Merge brand discounts into itemDiscounts
        // Store them in a special property for later matching
        result._brandDiscounts = brandDiscounts;
        
        // Also merge into itemDiscounts for backward compatibility
        if (Object.keys(brandDiscounts).length > 0) {
          result.itemDiscounts = { ...result.itemDiscounts, ...brandDiscounts };
        }
        
        return result;
      };
      VAI_QR.parsePrompt._patchedBrandDiscount = true;
      console.log('[Comprehensive Fix] Patched VAI_QR.parsePrompt for brand-level discounts');
    }

    // =====================================================================
    // 2. PATCH matchItemDiscount TO HANDLE BRAND MATCHING
    // =====================================================================
    if (VAI_QR.matchItemDiscount && !VAI_QR.matchItemDiscount._patchedBrandMatch) {
      const origMatchItemDiscount = VAI_QR.matchItemDiscount;
      
      VAI_QR.matchItemDiscount = function(it, itemDiscounts) {
        // First try original matching (SKU/model/code matching)
        const originalResult = origMatchItemDiscount.call(this, it, itemDiscounts);
        if (originalResult > 0) return originalResult;
        
        // If no match, try brand matching from _brandDiscounts
        // Check if itemDiscounts has brand keys (lowercase, no special chars)
        const brandKeys = Object.keys(itemDiscounts || {}).filter(k => 
          !k.match(/[A-Za-z]{1,8}[A-Za-z0-9._\-\/]{1,30}\d/) // Not a product code pattern
        );
        
        if (brandKeys.length === 0) return 0;
        
        // Get item's brand from product data
        let itemBrand = '';
        if (it.brand) {
          itemBrand = it.brand.toLowerCase();
        } else if (window.D) {
          // Try to find product in D array
          const idx = it.idx != null ? it.idx : (it.productIdx != null ? it.productIdx : null);
          let product = null;
          if (idx != null && window.D[idx]) {
            product = window.D[idx];
          } else if (it.model || it.sku || it.ma || it.name) {
            const ck = (it.model || it.sku || it.ma || it.name || '').toString().toLowerCase().replace(/[^a-z0-9]/g, '');
            product = window.D.find(x => {
              const xck = (x.model || x.sku || x.name || '').toString().toLowerCase().replace(/[^a-z0-9]/g, '');
              return xck.includes(ck) || ck.includes(xck);
            });
          }
          if (product && product.brand) {
            itemBrand = product.brand.toLowerCase();
          }
        }
        
        // Match against brand discounts
        const cleanItemBrand = itemBrand.replace(/[^a-z0-9]/g, '');
        for (const bk of brandKeys) {
          const cleanBk = bk.replace(/[^a-z0-9]/g, '');
          if (cleanBk && cleanItemBrand && (cleanItemBrand.includes(cleanBk) || cleanBk.includes(cleanItemBrand))) {
            return itemDiscounts[bk];
          }
        }
        
        return 0;
      };
      VAI_QR.matchItemDiscount._patchedBrandMatch = true;
      console.log('[Comprehensive Fix] Patched VAI_QR.matchItemDiscount for brand matching');
    }

    // =====================================================================
    // 3. PATCH applyPromptDiscounts TO PROPAGATE BRAND DISCOUNTS
    // =====================================================================
    if (VAI_QR.applyPromptDiscounts && !VAI_QR.applyPromptDiscounts._patchedBrand) {
      const origApplyPromptDiscounts = VAI_QR.applyPromptDiscounts;
      
      VAI_QR.applyPromptDiscounts = function(qd, parsed) {
        // Call original
        const result = origApplyPromptDiscounts.call(this, qd, parsed);
        
        // If we have brand discounts but no item-level discounts were applied,
        // apply them to matching items
        if (parsed && parsed._brandDiscounts && Object.keys(parsed._brandDiscounts).length > 0) {
          if (result && result.items) {
            result.items.forEach(it => {
              // Check if this item already has a discount
              if (!it.discountPercent || it.discountPercent === 0) {
                // Try to match brand
                let itemBrand = '';
                if (it.brand) {
                  itemBrand = it.brand.toLowerCase();
                } else if (window.D) {
                  const idx = it.idx != null ? it.idx : (it.productIdx != null ? it.productIdx : null);
                  let product = null;
                  if (idx != null && window.D[idx]) {
                    product = window.D[idx];
                  } else if (it.model || it.sku || it.ma || it.name) {
                    const ck = (it.model || it.sku || it.ma || it.name || '').toString().toLowerCase().replace(/[^a-z0-9]/g, '');
                    product = window.D.find(x => {
                      const xck = (x.model || x.sku || x.name || '').toString().toLowerCase().replace(/[^a-z0-9]/g, '');
                      return xck.includes(ck) || ck.includes(xck);
                    });
                  }
                  if (product && product.brand) {
                    itemBrand = product.brand.toLowerCase();
                  }
                }
                
                const cleanItemBrand = itemBrand.replace(/[^a-z0-9]/g, '');
                for (const [brand, pct] of Object.entries(parsed._brandDiscounts)) {
                  const cleanBrand = brand.replace(/[^a-z0-9]/g, '');
                  if (cleanBrand && cleanItemBrand && (cleanItemBrand.includes(cleanBrand) || cleanBrand.includes(cleanItemBrand))) {
                    it.discPrice = Math.round((it.price || 0) * (1 - pct / 100));
                    it.discountPercent = pct;
                    it.total = (it.discPrice || it.price || 0) * (it.qty || 1);
                    it._promptDiscount = true;
                    break;
                  }
                }
              }
            });
            
            // Recalculate grandTotal
            let productTotal = 0;
            result.items.forEach(it => {
              productTotal += Number(it.total || 0);
            });
            result.grandTotal = productTotal;
          }
        }
        
        return result;
      };
      VAI_QR.applyPromptDiscounts._patchedBrand = true;
      console.log('[Comprehensive Fix] Patched VAI_QR.applyPromptDiscounts for brand discounts');
    }

    // =====================================================================
    // 4. PATCH _doExportExcel FOR FORMULA-BASED DISCOUNT & NO NOTES ROW
    // =====================================================================
    if (VAI_QR.exportExcel && !VAI_QR.exportExcel._patchedComprehensive) {
      const orig = VAI_QR.exportExcel;
      
      VAI_QR.exportExcel = async function(d, qd, code, qr) {
        // Ensure items have correct discPrice from prompt discounts
        if (qd && qd.items) {
          qd.items.forEach((it, i) => {
            // If item has discountPercent from prompt, ensure discPrice reflects it
            if (it.discountPercent && it.discountPercent > 0 && it.price) {
              it.discPrice = Math.round(it.price * (1 - it.discountPercent / 100));
              it.total = it.discPrice * (it.qty || 1);
            }
            // Mark that this item has prompt-based discount (for formula)
            it._promptDiscount = (it.discountPercent && it.discountPercent > 0) || 
                                 (d.itemDiscounts && matchItemDiscountForPrompt(it, d.itemDiscounts)) ||
                                 (d._brandDiscounts && matchBrandDiscountForPrompt(it, d._brandDiscounts));
          });
        }
        
        // SUPPRESS NOTES ROW - set d.notes to empty array
        // Fees (giao hàng, lắp đặt, cọc, bốc xếp) are in d.fees and SHOULD appear
        const originalNotes = d.notes;
        d.notes = [];
        
        try {
          return await orig.call(this, d, qd, code, qr);
        } finally {
          // Restore
          d.notes = originalNotes;
        }
      };
      VAI_QR.exportExcel._patchedComprehensive = true;
      console.log('[Comprehensive Fix] Patched VAI_QR.exportExcel for formula discount & no notes');
    }

    // Helper: Check if item has prompt-based item discount (product code matching)
    function matchItemDiscountForPrompt(it, itemDiscounts) {
      if (!itemDiscounts || !it) return false;
      const keys = (it.model || it.sku || it.ma || it.name || '').toString().toLowerCase().replace(/[^a-z0-9]/g, '');
      for (const code in itemDiscounts) {
        const ck = code.toLowerCase().replace(/[^a-z0-9]/g, '');
        if (ck && keys && (keys.includes(ck) || ck.includes(keys)) && 
            code.match(/[A-Za-z]{1,8}[A-Za-z0-9._\-\/]{1,30}\d/)) { // product code pattern
          return true;
        }
      }
      return false;
    }

    // Helper: Check if item has prompt-based brand discount
    function matchBrandDiscountForPrompt(it, brandDiscounts) {
      if (!brandDiscounts || !it) return false;
      let itemBrand = '';
      if (it.brand) {
        itemBrand = it.brand.toLowerCase();
      } else if (window.D) {
        const idx = it.idx != null ? it.idx : (it.productIdx != null ? it.productIdx : null);
        let product = null;
        if (idx != null && window.D[idx]) {
          product = window.D[idx];
        } else if (it.model || it.sku || it.ma || it.name) {
          const ck = (it.model || it.sku || it.ma || it.name || '').toString().toLowerCase().replace(/[^a-z0-9]/g, '');
          product = window.D.find(x => {
            const xck = (x.model || x.sku || x.name || '').toString().toLowerCase().replace(/[^a-z0-9]/g, '');
            return xck.includes(ck) || ck.includes(xck);
          });
        }
        if (product && product.brand) {
          itemBrand = product.brand.toLowerCase();
        }
      }
      const cleanItemBrand = itemBrand.replace(/[^a-z0-9]/g, '');
      for (const brand in brandDiscounts) {
        const cleanBrand = brand.replace(/[^a-z0-9]/g, '');
        if (cleanBrand && cleanItemBrand && (cleanItemBrand.includes(cleanBrand) || cleanBrand.includes(cleanItemBrand))) {
          return true;
        }
      }
      return false;
    }

    // =====================================================================
    // 5. PATCH _doExportDeliveryExcel FOR NO NOTES ROW
    // =====================================================================
    if (VAI_QR.exportDeliveryExcel && !VAI_QR.exportDeliveryExcel._patchedComprehensive) {
      const orig2 = VAI_QR.exportDeliveryExcel;
      
      VAI_QR.exportDeliveryExcel = async function(qd, code) {
        // Suppress notes row
        const originalNotes = qd.notes;
        qd.notes = [];
        
        try {
          return await orig2.call(this, qd, code);
        } finally {
          qd.notes = originalNotes;
        }
      };
      VAI_QR.exportDeliveryExcel._patchedComprehensive = true;
      console.log('[Comprehensive Fix] Patched VAI_QR.exportDeliveryExcel for no notes');
    }

    // =====================================================================
    // 6. PATCH getData TO MARK PROMPT DISCOUNTS
    // =====================================================================
    if (VAI_QR.getData && !VAI_QR.getData._patchedComprehensive) {
      const origGetData = VAI_QR.getData;
      
      VAI_QR.getData = function() {
        const result = origGetData.apply(this, arguments);
        
        // Mark items with prompt discount (global, item-level, or brand-level)
        if (result.qd && result.qd.items && (result.itemDiscounts || result.discountPercent || result._brandDiscounts)) {
          result.qd.items.forEach(it => {
            if (it.discountPercent && it.discountPercent > 0) {
              it._promptDiscount = true;
            }
          });
        }
        
        // The notes are already in result.notes (from parsed.notes)
        // Excel patch will suppress them by setting d.notes = []
        // Fees in result.fees will still appear (giao hàng, lắp đặt, cọc, bốc xếp)
        
        return result;
      };
      VAI_QR.getData._patchedComprehensive = true;
      console.log('[Comprehensive Fix] Patched VAI_QR.getData for prompt discount marking');
    }

    // =====================================================================
    // 7. PATCH buildQuoteHTML (PDF/Image export) TO SUPPRESS NOTES
    // =====================================================================
    if (VAI_QR.buildQuoteHTML && !VAI_QR.buildQuoteHTML._patchedComprehensive) {
      const origBuildQuoteHTML = VAI_QR.buildQuoteHTML;
      
      VAI_QR.buildQuoteHTML = function(d, code, qrUrl) {
        // Suppress notes in HTML export too
        const originalNotes = d.notes;
        d.notes = [];
        
        try {
          return origBuildQuoteHTML.call(this, d, code, qrUrl);
        } finally {
          d.notes = originalNotes;
        }
      };
      VAI_QR.buildQuoteHTML._patchedComprehensive = true;
      console.log('[Comprehensive Fix] Patched VAI_QR.buildQuoteHTML for no notes');
    }

    // =====================================================================
    // 8. PATCH buildDeliveryHTML (PDF/Image export) TO SUPPRESS NOTES
    // =====================================================================
    if (VAI_QR.buildDeliveryHTML && !VAI_QR.buildDeliveryHTML._patchedComprehensive) {
      const origBuildDeliveryHTML = VAI_QR.buildDeliveryHTML;
      
      VAI_QR.buildDeliveryHTML = function(d, code, qrUrl) {
        // Suppress notes in delivery HTML export too
        const originalNotes = d.notes;
        d.notes = [];
        
        try {
          return origBuildDeliveryHTML.call(this, d, code, qrUrl);
        } finally {
          d.notes = originalNotes;
        }
      };
      VAI_QR.buildDeliveryHTML._patchedComprehensive = true;
      console.log('[Comprehensive Fix] Patched VAI_QR.buildDeliveryHTML for no notes');
    }

    console.log('[Comprehensive Fix] All 8 patches applied successfully!');
  });
})();