feat: dual pricing policies + discount VAT splitting

Add production pricing policy (id_pol_productie) for articles with cont 341/345,
smart discount VAT splitting across multiple rates, per-article id_pol support,
and mapped SKU price validation. Settings UI updated with new controls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-03-18 15:10:05 +00:00
parent 69a3088579
commit 695dafacd5
11 changed files with 428 additions and 59 deletions

View File

@@ -60,18 +60,81 @@ def format_address_for_oracle(address: str, city: str, region: str) -> str:
return f"JUD:{region_clean};{city_clean};{address_clean}"
def compute_discount_split(order, settings: dict) -> dict | None:
"""Compute proportional discount split by VAT rate from order items.
Returns: {"11": 3.98, "21": 1.43} or None if split not applicable.
Only splits when split_discount_vat is enabled AND multiple VAT rates exist.
When single VAT rate: returns {actual_rate: total} (smarter than GoMag's fixed 21%).
"""
if not order or order.discount_total <= 0:
return None
split_enabled = settings.get("split_discount_vat") == "1"
# Calculate VAT distribution from order items (exclude zero-value)
vat_totals = {}
for item in order.items:
item_value = abs(item.price * item.quantity)
if item_value > 0:
vat_key = str(int(item.vat)) if item.vat == int(item.vat) else str(item.vat)
vat_totals[vat_key] = vat_totals.get(vat_key, 0) + item_value
if not vat_totals:
return None
grand_total = sum(vat_totals.values())
if grand_total <= 0:
return None
if len(vat_totals) == 1:
# Single VAT rate — use that rate (smarter than GoMag's fixed 21%)
actual_vat = list(vat_totals.keys())[0]
return {actual_vat: round(order.discount_total, 2)}
if not split_enabled:
return None
# Multiple VAT rates — split proportionally
result = {}
discount_remaining = order.discount_total
sorted_rates = sorted(vat_totals.keys(), key=lambda x: float(x))
for i, vat_rate in enumerate(sorted_rates):
if i == len(sorted_rates) - 1:
split_amount = round(discount_remaining, 2) # last gets remainder
else:
proportion = vat_totals[vat_rate] / grand_total
split_amount = round(order.discount_total * proportion, 2)
discount_remaining -= split_amount
if split_amount > 0:
result[vat_rate] = split_amount
return result if result else None
def build_articles_json(items, order=None, settings=None) -> str:
"""Build JSON string for Oracle PACK_IMPORT_COMENZI.importa_comanda.
Includes transport and discount as extra articles if configured."""
Includes transport and discount as extra articles if configured.
Supports per-article id_pol from codmat_policy_map and discount VAT splitting."""
articles = []
codmat_policy_map = settings.get("_codmat_policy_map", {}) if settings else {}
default_id_pol = settings.get("id_pol", "") if settings else ""
for item in items:
articles.append({
article_dict = {
"sku": item.sku,
"quantity": str(item.quantity),
"price": str(item.price),
"vat": str(item.vat),
"name": clean_web_text(item.name)
})
}
# Per-article id_pol from dual-policy validation
item_pol = codmat_policy_map.get(item.sku)
if item_pol and str(item_pol) != str(default_id_pol):
article_dict["id_pol"] = str(item_pol)
articles.append(article_dict)
if order and settings:
transport_codmat = settings.get("transport_codmat", "")
@@ -90,20 +153,50 @@ def build_articles_json(items, order=None, settings=None) -> str:
if settings.get("transport_id_pol"):
article_dict["id_pol"] = settings["transport_id_pol"]
articles.append(article_dict)
# Discount total with quantity -1 (positive price)
# Discount — smart VAT splitting
if order.discount_total > 0 and discount_codmat:
# Use GoMag JSON discount VAT if available, fallback to settings
discount_vat = getattr(order, 'discount_vat', None) or settings.get("discount_vat", "19")
article_dict = {
"sku": discount_codmat,
"quantity": "-1",
"price": str(order.discount_total),
"vat": discount_vat,
"name": "Discount"
}
if settings.get("discount_id_pol"):
article_dict["id_pol"] = settings["discount_id_pol"]
articles.append(article_dict)
discount_split = compute_discount_split(order, settings)
if discount_split and len(discount_split) > 1:
# Multiple VAT rates — multiple discount lines
for vat_rate, split_amount in sorted(discount_split.items(), key=lambda x: float(x[0])):
article_dict = {
"sku": discount_codmat,
"quantity": "-1",
"price": str(split_amount),
"vat": vat_rate,
"name": f"Discount (TVA {vat_rate}%)"
}
if settings.get("discount_id_pol"):
article_dict["id_pol"] = settings["discount_id_pol"]
articles.append(article_dict)
elif discount_split and len(discount_split) == 1:
# Single VAT rate — use detected rate
actual_vat = list(discount_split.keys())[0]
article_dict = {
"sku": discount_codmat,
"quantity": "-1",
"price": str(order.discount_total),
"vat": actual_vat,
"name": "Discount"
}
if settings.get("discount_id_pol"):
article_dict["id_pol"] = settings["discount_id_pol"]
articles.append(article_dict)
else:
# Fallback — original behavior with GoMag VAT or settings default
discount_vat = getattr(order, 'discount_vat', None) or settings.get("discount_vat", "21")
article_dict = {
"sku": discount_codmat,
"quantity": "-1",
"price": str(order.discount_total),
"vat": discount_vat,
"name": "Discount"
}
if settings.get("discount_id_pol"):
article_dict["id_pol"] = settings["discount_id_pol"]
articles.append(article_dict)
return json.dumps(articles)