| """Vendor module β per-vendor rollup + the multiple-vendor / cheapest-price view. |
| |
| PURE: it takes the procurement recs (which already merge the curated supplier map + live Odoo |
| demand/stock + Odoo's alternative vendors via product.supplierinfo) and groups them by vendor, so |
| there is NO extra Odoo round-trip β the Procurement page and this page share one cached pull. Vendor |
| master-data edits (country / lead) persist through the procurement overrides store, applied to every |
| SKU of that vendor (the UI does the bulk write). READ-ONLY on Odoo. |
| """ |
|
|
|
|
| def rollup(recs): |
| """[{vendor, country, lead, n_skus, n_buy, demand_units, buy_value, lines}], by SKU count desc.""" |
| out = {} |
| for r in recs: |
| v = r.get('vendor') or '(no supplier)' |
| g = out.setdefault(v, {'vendor': v, 'country': r.get('country', ''), 'lead': r.get('lead', 0), |
| 'n_skus': 0, 'n_buy': 0, 'demand_units': 0.0, 'buy_value': 0.0, 'lines': []}) |
| g['n_skus'] += 1 |
| if r.get('buy'): |
| g['n_buy'] += 1 |
| g['demand_units'] += (r.get('demand_8m') or 0) |
| g['buy_value'] += (r.get('reorder_trend') or 0) * (r.get('unit_cost') or 0) |
| g['lines'].append(r) |
| if not g['country'] and r.get('country'): |
| g['country'] = r['country'] |
| if not g['lead'] and r.get('lead'): |
| g['lead'] = r['lead'] |
| return sorted(out.values(), key=lambda x: (-x['n_skus'], x['vendor'] or '~')) |
|
|
|
|
| def multi_vendor(recs): |
| """SKUs Odoo lists with >1 vendor β current vendor/cost vs the cheapest alternative (price-shop).""" |
| out = [] |
| for r in recs: |
| if (r.get('n_vendors') or 0) <= 1: |
| continue |
| cur = r.get('unit_cost') or 0 |
| cheapest = r.get('cheapest_price') |
| cheaper = bool(cheapest is not None and cur > 0 and cheapest < cur - 1e-9) |
| out.append({**r, 'current_cost': cur, |
| 'savings': (cur - cheapest) if cheaper else 0.0, 'cheaper': cheaper}) |
| out.sort(key=lambda r: -r['savings']) |
| return out |
|
|