vertiq
Wholesale and distribution

Wholesale and distribution

Identify accounts with declining orders, predict late payments, and recommend the next SKU per account.

Data mapping

Entity
account
Group
sales region
Profile
b2b
SOURCE COLUMNSVERTIQ DATA CONTRACTCuentaFecha FacturaTipo DocNet ValueSales OrgPayment TermsSales RepDue DatePaid DateEntity · identifierEvent · timestampEvent · typeEvent · valueEntity · groupEvent · attributes%d.%m.%Y
Vertiq LM can propose this mapping from the header and sample rows. Column names, value types and the date format are validated against the data before use.

Supported analyses

Reference implementation

from datetime import datetime, timedelta
from vertiq import plugins, rules
from vertiq.mapping import Mapping, apply, repair
from vertiq.tasks import predict_event, predict_value

# rows: the source export as a list of dicts, e.g. list(csv.DictReader(open("export.csv")))
mapping = Mapping.parse({"entity": "Cuenta", "time": "Fecha Factura", "type": "Tipo Doc", "value": "Net Value", "group": "Sales Org", "attrs": ["Payment Terms", "Sales Rep", "Due Date", "Paid Date"], "date_format": "%d.%m.%Y"})
m, notes = repair(mapping, rows)      # validates column names, value types and date format against the rows
ds, problems = apply(m, rows)         # rows that fail to load are reported, not imputed

@plugins.feature("b2b")
def credit_notes_180(entity, before, cut):   # receives only events recorded before the cutoff
    return {"crn_180": sum(1 for e in before if e.type == "CRN"
                          and e.at >= cut - timedelta(days=180))}

task = predict_event(ds, target="INV", window_days=90, pack="b2b")
print(task.status, task.report())     # "trained" only if the model outperformed the baseline on held-out entities
if task.status == "trained":
    print(task.score("ACC4399"))  # probability with contributing factors

size = predict_value(ds, target="INV", window_days=90, pack="b2b")
print(size.status, size.report())

flags = rules.run(ds, [
    rules.overdue("Due Date", "Paid Date", tolerance_days=15),   # invoices paid more than 15 days after the due date
])
print(flags["summary"])

This code is executed by the test suite (tests/test_guides.py) against generated records with these column names.

Recommended plugin

credit_notes_180: Credit notes often precede declining order volume.

Validation rules

  • rules.overdue("Due Date", "Paid Date", tolerance_days=15): invoices paid more than 15 days after the due date

Related modules

  • affinity: SKUs ordered together and the next likely SKU per account
  • value.campaign_value: incremental revenue from a campaign compared with its control accounts

Implementation notes

  • Map credit notes as a separate event type so they are not counted as orders.

Validation

  • Invoice tables with German, Spanish and French headers in the Vertiq LM mapping evaluation
  • Every deployment: the evaluation gate measures each model on the organisation's own held-out records before promotion.
← Software and subscriptionsAccounting and tax compliance →