Carry 'INCL' tariffs through instead of arriving blank

The engineer reported that some of his tariffs on Blueberry Square were saying
'included' and did not pull through. He was right, and it was not a big issue
for the wrong reason - it was a real gap.

Candy prints a WORD in the rate column where a tariff is carried inside a sum
above rather than priced separately - INCL, incl VAT, PROV. Those lines have a
quantity but no figure, so there was nothing numeric to write and the cells
arrived BLANK. To the consultant that is indistinguishable from a failed
conversion.

The source's own wording is now written into the rate cell and the line is
listed as a note:

  F15 = 'INCL'   a) Office buildings
  F17 = 'INCL'   b) Survey Equipment

20 such lines on the Blueberry pair. The amount column is left alone because
there genuinely is no figure for that line - the money is in the sum above it.

Caught a placement bug doing it: the write sat after the early return for lines
whose quantity already matched, which is most of them, so it fired zero times.
Moved it before the quantity block.

Both pairs re-verified after: formulas intact, 0 damaged, gate not blocked.
This commit is contained in:
MEGA INFRA 2026-10-04 00:24:33 +02:00
parent a9b74e5859
commit 848f3093ea
3 changed files with 48 additions and 0 deletions

View file

@ -232,6 +232,25 @@ checked rather than intended. On the SANS 1200 template, where quantities are
derived (`=G35`, `=CEILING.MATH(...)`), writing a literal over that would
destroy the engineer's model while looking like it worked.
## Tariffs carried inside a sum
Candy sometimes prints a **word where a rate would go** — `INCL`, `Incl VAT`,
`PROV` — meaning that tariff is included in the sum above rather than priced
separately. Those lines have a quantity but no figure, and they used to arrive
in the consultant's file as **blank cells**, which read as a failure to pull
through. That is exactly what the engineer reported.
They now carry the source's own wording into the rate cell, and are listed as a
note rather than a disagreement:
```
F15 = 'INCL' a) Office buildings
F17 = 'INCL' b) Survey Equipment
```
The amount column is left alone, because there is genuinely no figure for that
line — the money is in the sum above it. 20 of these on the Blueberry pair.
## A person decides the rest
The aligner leaves some lines unplaced because nothing scored high enough, and

View file

@ -147,6 +147,11 @@ class Row:
qty: float | None = None
rate: float | None = None
amount_txt: str = ""
# Candy prints a WORD in the rate/amount column when a tariff is carried
# inside a sum rather than priced separately - "INCL", "Incl VAT", "PROV".
# That is a real tariff decision, not a missing number, and it has to
# survive into the consultant's file instead of arriving as a blank.
tariff_note: str = ""
top: float = 0.0
page: int = 0
@ -196,9 +201,13 @@ def _page_rows(page, pno: int) -> list[Row]:
continue
if col == "rate" and r.rate is None:
r.rate = _to_num(txt)
if r.rate is None and not txt.replace(".", "").isdigit():
r.tariff_note = (r.tariff_note + " " + txt).strip()
continue
if col == "amount":
r.amount_txt = (r.amount_txt + " " + txt).strip()
if _to_num(txt) is None and not txt.replace(".", "").isdigit():
r.tariff_note = (r.tariff_note + " " + txt).strip()
continue
if UNIT_X_MIN <= x0 < UNIT_X_MAX:
r.unit = (r.unit + txt).strip()
@ -348,6 +357,7 @@ def _merge_continuations(rows: list[Row]) -> list[Row]:
@dataclass
class Line:
kind: str = "line"
tariff_note: str = ""
number: str = ""
desc: str = ""
unit: str = ""
@ -505,6 +515,7 @@ def _build(content: list[Row]) -> Group:
and t.depth(num) >= 3
line = Line(
tariff_note=r.tariff_note,
number=num,
desc=f"{sub}) {body}" if sub else body,
unit=r.unit.strip(),

View file

@ -507,6 +507,7 @@ def source_lines(tree: dict, *, with_prices: bool = True) -> list[dict]:
return
out.append({
"number": node.get("number", "") or "",
"tariff_note": (node.get("tariff_note") or "").strip(),
"desc": (node.get("desc") or "").strip(),
"unit": (node.get("unit") or "").strip(),
"qty": node.get("qty"),
@ -885,6 +886,23 @@ def _write_one(wb, name: str, lay: Layout, ln: dict,
field="unit", template_value=cur,
source_value=ln["unit"])
# --- a tariff carried inside a sum ("INCL", "incl VAT") -----------------
# Written as the literal text the source printed. A blank cell here reads
# as a failure to the consultant; the source's own wording is the honest
# answer, and the amount column is left alone because there is no figure.
note = (ln.get("tariff_note") or "").strip()
if note and ln["rate"] is None and c_rate and \
ws.cell(r, c_rate).value in (None, ""):
if _writable(ws, r, c_rate, res, name, label, "rate"):
ws.cell(r, c_rate, note)
res.wrote(name, r, c_rate, note)
res.written += 1
res.add("info", name, r, label,
f"carried in the sum - the source prints \"{note}\" rather "
f"than a rate, so that wording was kept. There is no amount "
f"for this line.",
source=note)
# --- quantity ------------------------------------------------------------
stop = False
if c_qty and ln["qty"] is not None: