Skip to content

TicketBAI: charge item with TypeOfService=OtherService is duplicated across Entrega and PrestacionServicios #673

Description

@StefanKert

Summary

In TicketBaiFactory.GetFacturaDetails, the foreign-customer branch filters charge items into two buckets — entregaChargeItems and prestacionServiciosChargeItems. ChargeItemCaseTypeOfService.OtherService is present in both filter lists, so any charge item whose TypeOfService() is OtherService is reported twice in the emitted XML: once under <Entrega> and once under <PrestacionServicios>. The same <BaseImponible> (or VAT base, or <Importe>) ends up appearing in both branches, which both inflates the totals AEAT cross-checks and produces an XML the recipient sees as two distinct operations.

Reproduction

Build a ProcessRequest with:

  • A foreign cbCustomer (e.g. CustomerCountry = \"FR\"),
  • A single ChargeItem with ftChargeItemCase carrying TypeOfService = OtherService (low byte 0x28) and any NatureOfVat,

and run TicketBaiFactory.ConvertTo. The produced <TipoDesglose><DesgloseTipoOperacion> has both an <Entrega> and a <PrestacionServicios> block, each containing the full charge-item amount.

Location

scu-es/src/fiskaltrust.Middleware.SCU.ES.TicketBAI.Common/TicketBaiFactory.cs, around the foreign-customer branch of GetFacturaDetails:

```csharp
var entregaChargeItems = new List {
ChargeItemCaseTypeOfService.UnknownService,
ChargeItemCaseTypeOfService.Delivery,
ChargeItemCaseTypeOfService.Voucher,
ChargeItemCaseTypeOfService.CatalogService,
ChargeItemCaseTypeOfService.NotOwnSales,
ChargeItemCaseTypeOfService.OtherService // ← also in prestacion list
};

var prestacionServiciosChargeItems = new List {
ChargeItemCaseTypeOfService.OtherService, // ← also in entrega list
ChargeItemCaseTypeOfService.Tip,
ChargeItemCaseTypeOfService.Grant,
ChargeItemCaseTypeOfService.Receivable,
ChargeItemCaseTypeOfService.CashTransfer
};
```

Pre-existing

This was introduced when the foreign-customer branch was first added in a1c14a66 "Support for foreign customers" and was preserved through the recent exempt-reasons refactor in #669 — the refactor passes the same filtered lists into the new BuildDesgloseParts helper.

Suggested fix

Decide which branch OtherService belongs in. The semantics in fiskaltrust.ifPOS.v2 are "a service of an unspecified kind", which strongly suggests PrestacionServicios (provision of services) — i.e. remove it from the entregaChargeItems list. If there are real use cases where an OtherService charge item should land in Entrega, the input model needs a more specific TypeOfService rather than overloaded routing.

Impact

Limited today because the TicketBaiFactory is itself only reachable for receipts that pass through the TicketBAI SCU (Araba / Bizkaia / Gipuzkoa), and the foreign-customer path is hit only when cbCustomer.CustomerCountry != \"ES\". But any foreign-customer invoice that uses OtherService charge items today produces a malformed AEAT submission, and the refactor in #669 makes the same shape produced for exempt cases too, so the doubling now also duplicates <CausaExencion> and <Causa> declarations — not just <DesgloseIVA>. Worth fixing before #669 ships.

Related

🤖 Filed via Claude Code on behalf of @StefanKert

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions