-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add basic structs for MX fuel complement #192
Conversation
cavalle
commented
Sep 1, 2023
•
edited
Loading
edited
- A very early draft PR to discuss the structure of the fuel complement in MX.
- Just the structs, completely unintegrated, for the moment. Validations, calculations and integration with the main GOBL document are pending.
- See the comments in the code for some design considerations.
@@ -0,0 +1,58 @@ | |||
// Package complements provides GOBL Complements for Mexican invoices | |||
package complements |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what's the best way of structuring complements. For the moment, I'm putting everything under a complements
package within the mx
path. Suggestions on how to better do this are welcome!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure... if you imagine a user who needs to set up complements for multiple countries, they'd have to deal with the namespace collisions of complements
in multiple places. Could be awkward. I'd also think about the final schema URL, I was thinking of: https://gobl.org/draft-0/regimes/mx/fuel-complement
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// FuelComplement provides detailed information about fuel purchases made with | ||
// electronic wallets. In Mexico, e-wallet suppliers are required to report this | ||
// information as part of the invoices they issue to their customers. | ||
type FuelComplement struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When creating the structs of this complement, I considered two general approaches:
-
Mirror the target CFDI structure: The first option was to replicate the CFDI fields and structure directly (not unlike what we've done with ext fields – straightforward and explicit).
-
Align with GOBL's idioms: I chose this. It should make the structs more intuitive for GOBL developers who may not be familiar with CFDIs, but will definitely be used to GOBL. It also ensures that the resulting GOBL document looks harmonious and not like a Frankenstein.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. I think we should at least try to smooth out the edges.
// List of fuel purchases made with the customer's e-wallets | ||
Lines []*FuelLine `json:"lines" jsonschema:"title=Lines"` | ||
// Summary of all the purchases totals, including taxes (calculated) | ||
Totals bill.Totals `json:"totals" jsonschema:"title=Totals" jsonschema_extras:"calculated=true"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm reusing bill.Totals
here but I'm not 100% sure about this as we only need the Total
and TotalWithTaxes
fields from that struct. It may be confusing for developers to have all the other fields available.
A penny for your thoughts!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm inclined to agree... this strikes me as a new structure alongside the complements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree! Addressed in 4be0288
|
||
// FuelLine represents a single fuel purchase made with an e-wallet issued by | ||
// the invoice's supplier to the invoice's customer. | ||
type FuelLine struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used the "line" word because the whole structure resembles very much to the bill.Line
struct. We could even extend the bill.Line
struct here with the specific fields we need in this context, which enable to potentially reuse part of the validation/calculation logic. However, I thought that binding the evolution of the bill.Line
struct and behaviour to this struct may not be a good idea if those diverge in the future. Also, like I mentioned in other comments, bill.Line
has fields that won't be used here, and that could confuse developers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're okay recreating the structures for this use-case. FuelLine makes sense.
// Identifier of the e-wallet used to make the purchase | ||
WalletID cbc.Code `json:"wallet-id" jsonschema:"title=Wallet Identifier"` | ||
// Date and time of the purchase | ||
PurchaseDateTime civil.DateTime `json:"purchase-date" jsonschema:"title=Purchase Date"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have cal.DateTime
at the moment. Should we add it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 interesting. It might indeed be a good idea to have a cal.DateTime
which enforces ISO formatting. We have a similar "at" package in github.com/invopop/lib which does something similar with database storage. I'll think a bit more about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok! Leaving civil.DateTime
for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you also considered two fields? I'm not convinced, and we'd still need a new type, but "Date" and "Time" types could make sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was right now drafting a cal.DateTime
type that would marshal and unmarshall in the ISO format. I personally don't see any major problem on having a DateTime type, it's quite universally and very well standardised. What concerns do you have? Separating Date
and Time
is definitely an option, but then we will need a new cal.Time
type too, wouldn't we?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just pushed 5a3388e with a barebones cal.DateTime
implementation (I'll complete it once we confirm that's the like we'd like to follow). The only concern I have about the type is whether or not it should support time zones. The CFDI expects a date and time without a timezone, but in the future we may need a Date and Time with Timezones for other purposes. Not sure we'll want to add just another type then.
} | ||
|
||
// FuelItem provides the details of the fuel purchased. | ||
type FuelItem struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed in other parts of this PR, the similarity of this struct with org.Item
makes it tempting to use it directly. In this particular case, I decided not to, but the considerations discussed in other comments could be applied here to make a different decision. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be tempted to store the FuelItem
fields inside the FuelLine
... the criteria is dependent on how re-usable the Item needs to be. Will the FuelItem
ever need to be persisted independently?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created a separate FuelItem
struct just to mirror the existing pattern in bill.Line
and org.Item
. It neatly separates item details from purchase info, and it's an idiom that GOBL developers will be familiar with. I also expect this data to be persisted separately in the source systems.
On the other hand, in the CFDI the line and item data are at the same level. So if you're not convinced by this approach, let me know and I'll flatten down here too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I accidentally deleted your last comment here. Sorry for the mess, I'm bringing it back:
@samlown said:
My thoughts are around the path of least resistance for users trying to figure out what to do. I think it might be a good idea in this case to stick as closely as possible to the original source structure, with translated fields...
Finding the path of least resistance for users is the same design principle I'm trying to follow. Question is which path is that! From my perspective, one of the main benefits that GOBL brings to developers is providing them with a single unified language, with a consistent vocabulary and set of idioms and patterns that they can use across regimes. That's why I thought the least resistance path was modelling lines and items resembling the ones in GOBL.
In any case, I'll propose an alternative version sticking as closely as possible to the original source structure with translated fields.
// Reference unit of measure used in the price and the quantity | ||
Unit org.Unit `json:"unit" jsonschema:"title=Unit"` | ||
// Type of fuel (c_ClaveTipoCombustible codes) | ||
Type cbc.Code `json:"type,omitempty" jsonschema:"title=Type"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are only a bunch of simple values that can be used here:
c_ClaveTipoCombustible | Tipo de combustible |
---|---|
1 | Gasolina menor a 92 octanos |
2 | Gasolina mayor o igual a 92 octanos |
3 | Diesel |
4 | Diesel Marino |
5 | Otros |
We can simply use a cbc.Code with explicit codes (and validate them), or go fancy and create a new type (FuelType
) that is a cbc.Key
and with valid values are more meaningful (diesel
, gasoline
…) like we do with the org.Units
and in many other places in GOBL.
I opted for the simple solution, but I can see the merits of the meaningful key option. Any thoughts about this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initial impression: I think unless there is a fuel type elsewhere, we can just use the code specific for this document. Given the local nature of this, I'm not as worried... I envisage most use-cases of this will be hard coded in code and very unlikely to use a front-end... my impression anyway.
// Result of quantity multiplied by the item's price (calculated) | ||
Sum num.Amount `json:"sum" jsonschema:"title=Sum" jsonschema_extras:"calculated=true"` | ||
// Map of taxes applied to the purchase | ||
Taxes tax.Set `json:"taxes" jsonschema:"title=Taxes"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reusing GOBL's tax.Set
. This is the case where reusing makes more sense to me, we need all the fields and can take advantage of the rate calculation using regime's data.
// information as part of the invoices they issue to their customers. | ||
type FuelComplement struct { | ||
// Customer's e-wallet account number | ||
WalletAccountNumber string `json:"wallet-account-number" jsonschema:"title=Wallet Account Number"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GOBL convention is to use _
(underscore) to separate words. The initial aim was to be database compatible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My oversight. Fixed on 7b4ed40. Good catch!
|
||
// FuelComplement provides detailed information about fuel purchases made with | ||
// electronic wallets. In Mexico, e-wallet suppliers are required to report this | ||
// information as part of the invoices they issue to their customers. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to reference here the exact name of the complement and version that will be generated as a result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Addressed in 653e8bc
d344e88
to
87305b0
Compare
@sam I just pushed more changes, merging main (with complements and datetimes), and flattening down FuelItem into FuelLine as discussed above. Let me know if you have any other feedback on the structure, naming, documentation of the complement when you can, please. |
// providing detailed information about fuel purchases made with electronic | ||
// wallets. In Mexico, e-wallet suppliers are required to report this | ||
// complementary information in the invoices they issue to their customers. | ||
type FuelComplement struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So after reading the official name, had you considered any alternative names? Perhaps EWalletFuelComplement
? I'm worried about potential colisions, and the fact that "FuelComplement" doesn't cover the wallet part...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering this name will also be part of the schema name, I tried to make it as short as possible. I suppose future name collisions are indeed a possibility (although probably small). In the XML, they call the root tag EstadoCuentaCombustibles
, so they give more priority to the "estado de cuenta" part of the name than to the "e-wallet" aspect. So here's a list of options with their trade-offs:
Name | Trade-offs |
---|---|
FuelComplement |
👍 short and to the point 👎 not fully meaningful, collision-able |
EWalletFuelComplement |
👍 meaningful, no collisions 👎 long schema name |
FuelAccountStatement |
👍 direct translation of EstadoCuentaCombustibles 👎 no mention of e-wallet, no mention of being a complement |
I don't love any of them, but I think all of them would work. Let me know if you have any preference (or any other suggestion) and we'll go with that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be inclined to go with the direct translation: FuelAccountStatement
or FuelAccountBalance
which is the translation I got from ChatGPT.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hehe, I got "Account Statement" from Deepl, but I like "Account Balance" better! (AIs FTW!)
Total num.Amount `json:"total" jsonschema:"title=Total"` | ||
// Grand total after taxes have been applied. | ||
TotalWithTax num.Amount `json:"total_with_tax" jsonschema:"title=Total with Tax"` | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth adding any extra validation? Even if just the basics of ensuring that each field is a valid value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure thing! I just wanted us to focus on the data structure of the GOBL complement. But yes, I'm planning to add validations and calculations also. I think we're reaching a point where the structure is generally agreed on. I'll start pushing other changes soon.
Superseded by #210 |