Key the set-price menu modes on the set instead of the service-request block (ADR 0019)
"Set mit Preis" / "Nur Set mit Preis" collapsed every block into an inserted "Gesamtumfang pauschal" row, nulled/removed all lines and cleared the user-defined set structure. Both modes now convert every still-unconverted top-level set: the set line takes the sum of its subtree, its members render without a price (setprice) or are removed from itm + items (setonly); lines outside a set stay individually priced and several sets stay separate lines. Nested sets are priced once via their top-level set. One conversion primitive (ConvertSetHeader) now backs all three triggers; item.setprice is a no-op on an already-converted set (a repeated delta used to reset the set price to 0). The header's price_net is mirrored too (flat PDF path unit price), and Build treats a null total_net as "no price" like the flat path. The editor checks for a set before offering the modes and the menu entry stays available for sets added later. ADR 0009 is superseded by 0019; INVOICE_SET_PRICING.md rewritten accordingly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -778,9 +778,11 @@ public static class FuchsPdf
|
||||
// Blocks without any set items are unaffected and always render flat.
|
||||
var setMode = InvoiceSetPricing.ModeFromInvoiceOptions(inv.InvoiceRegistration?.getString("InvoiceOptions"));
|
||||
|
||||
// Function-1 (SetItmId-linked) sets still collapse via Build; block-mode sets (ADR 0009)
|
||||
// and plain blocks render flat — their set row is emphasised on its own and nulled
|
||||
// members map to blank cells (see MapItemToLine).
|
||||
// Blocks that carry a set (a line pointing at a set header via setId) render through Build,
|
||||
// which blanks/drops the members of a converted set; blocks without any set render flat —
|
||||
// nulled prices map to blank cells either way (see MapItemToLine). Since ADR 0019 the menu
|
||||
// modes convert per set (not per block) and leave non-set lines individually priced, so both
|
||||
// paths print exactly what the editor shows.
|
||||
var blockLines = inv.InvoiceBlocks
|
||||
.Select(block => InvoiceSetPricing.HasSetMembers(block.Items)
|
||||
? InvoiceSetPricing.Build(block.Items, setMode)
|
||||
|
||||
@@ -97,10 +97,10 @@ public static class InvoiceSetPricing
|
||||
items.Any(IsSetHeader) || items.Any(i => !string.IsNullOrEmpty(SetIdOf(i)));
|
||||
|
||||
/// <summary>
|
||||
/// True if the block carries a <c>SetItmId</c>-linked set (a member points at a header via
|
||||
/// <c>setId</c>) — i.e. the function-1 "set-item switch" grouping. This is the only case that
|
||||
/// still routes through <see cref="Build"/>; the two menu modes (ADR 0009) clear <c>setId</c>
|
||||
/// and render flat (their inserted set row is emphasised on its own, members null out to blank).
|
||||
/// True if the block carries a set (a line points at a set header via <c>setId</c>) — the only
|
||||
/// case that routes through <see cref="Build"/>. Since ADR 0019 every set-price operation — the
|
||||
/// inline set-item switch and both menu modes — preserves that membership, so this is what
|
||||
/// renders their result too; a block of lines that belong to no set renders flat.
|
||||
/// </summary>
|
||||
public static bool HasSetMembers(IEnumerable<Dictionary<string, object?>> items) =>
|
||||
items.Any(i => !string.IsNullOrEmpty(SetIdOf(i)));
|
||||
@@ -227,6 +227,10 @@ public static class InvoiceSetPricing
|
||||
{
|
||||
FuchsPdf.ParseDec(i.no("price_net", 0), out decimal price);
|
||||
FuchsPdf.ParseDec(i.no("total_net", 0), out decimal total);
|
||||
// A JSON-null total_net is "no price" (ADR 0009/0019) — an empty cell, never 0,00 € — exactly
|
||||
// as the flat path (FuchsPdf.MapItemToLine) treats it. Matters for a price-nulled member whose
|
||||
// set was later hand-edited back to 0 (and so reads as unconverted again).
|
||||
bool hasPrice = !i.TryGetValue("total_net", out var tv) || tv != null;
|
||||
return new InvoiceSetLine
|
||||
{
|
||||
Id = i.nz("id", ""),
|
||||
@@ -235,7 +239,7 @@ public static class InvoiceSetPricing
|
||||
Qty = i.nz("qty", "1"),
|
||||
PriceNet = price,
|
||||
TotalNet = total,
|
||||
ShowPrice = showPrice && !IsNoPriceLine(i), // headings/free text print no price
|
||||
ShowPrice = showPrice && hasPrice && !IsNoPriceLine(i), // headings/free text print no price
|
||||
Numbered = !IsNoPriceLine(i), // …but are still numbered unless heading/free-text
|
||||
IsSetHeader = false,
|
||||
Level = level
|
||||
|
||||
Reference in New Issue
Block a user