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:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user