Repository navigation
[16.0][FIX] pms_autoinvoice, pms_downpayment_lifecycle: down payments in automatic invoices - #170
Merged
Conversation
…it fits The automatic invoice deducts the folio's down payments from the invoice of their customer, and that step had several defects: - folio.sale.line._prepare_invoice_line() maps the taxes through the invoice fiscal position and has no default for it. The call did not pass it, so any automatic invoice with a down payment of its own customer failed with "'NoneType' object has no attribute 'map_tax'". - A down payment was added to every invoice of its customer. A folio split in two invoices for the same customer got it deducted twice, or failed with "Expected singleton". - It was added even when it left the invoice negative. With delay_post the negative draft was committed and its posting job then failed, leaving a draft that cannot be posted. - When a simplified invoice over the limit is moved to a named host, the down payment was still deducted from what was left for the original customer, while the host's invoice, the one billing the stay, settles it as well. Each down payment is now deducted at most once per run, from the largest invoice of its customer that it fits in without going negative. It is left to the final invoice when its customer was moved to a host, and when it fits nowhere, in which case the folio says so.
… the transfer pms_autoinvoice reverses the down payments that the final invoice did not deduct with cancel=True, which unreconciles them from their payment. For a down payment of a locked period that rewrites the customer balances of a month that is already closed. The _reverse_downpayment_invoices hook now leaves those down payments to the transfer, which settles them when the final invoice is posted: credit note with cancel=False and a transfer entry, so the closed period keeps its reconciliations. Down payments of an open period are reversed as before.
Diff CoverageDiff: origin/16.0...HEAD, staged and unstaged changes
Summary
pms_autoinvoice/models/pms_property.pyLines 220-235 220 to look at.
221 """
222 not_fitting = self.env["folio.sale.line"]
223 for downpayment in downpayments:
! 224 partner = downpayment.default_invoice_to
! 225 if partner in switched_partners:
! 226 continue
! 227 candidates = invoices.filtered(lambda inv, p=partner: inv.partner_id == p)
! 228 if not candidates:
! 229 continue
! 230 for invoice in candidates.sorted("amount_total", reverse=True):
! 231 vals = downpayment._prepare_invoice_line(
232 sequence=max(invoice.invoice_line_ids.mapped("sequence")) + 1,
233 # pms maps the taxes through the invoice fiscal position
234 # and has no default for it.
235 invoice_fpos=invoice.fiscal_position_id,Lines 233-252 233 # pms maps the taxes through the invoice fiscal position
234 # and has no default for it.
235 invoice_fpos=invoice.fiscal_position_id,
236 )
! 237 invoice.write({"invoice_line_ids": [(0, 0, vals)]})
! 238 if invoice.currency_id.compare_amounts(invoice.amount_total, 0.0) >= 0:
! 239 break
240 # It would leave the invoice negative: take it back out.
! 241 deduction = invoice.invoice_line_ids.filtered(
242 lambda line, d=downpayment: d in line.folio_line_ids
243 )
! 244 invoice.write(
245 {"invoice_line_ids": [(2, line.id) for line in deduction]}
246 )
247 else:
! 248 not_fitting |= downpayment
249 return not_fitting
250
251 def autoinvoice_folio(self, folio, delay_post=False):
252 try:Lines 314-322 314 hosts_to_invoice = all_invoice_partners.filtered(
315 lambda p: p._check_enought_invoice_data()
316 ).mapped("id")
317 if hosts_to_invoice:
! 318 switched_partners |= invoice.partner_id
319 invoice.partner_id = hosts_to_invoice[0]
320 invoice.journal_id = (
321 invoice.pms_property_id.journal_normal_invoice_id
322 )Lines 332-340 332 not_fitting = self._autoinvoice_deduct_downpayments(
333 invoices, downpayments, switched_partners
334 )
335 if not_fitting:
! 336 folio.sudo().message_post(
337 body=_(
338 "Down payments not deducted in the automatic invoice, "
339 "because they are larger than what it bills: %s. "
340 "They are left for review."pms_downpayment_lifecycle/models/account_move.pyLines 398-406 398 context the AEAT rejects the credit note (TipoRectificativa). The
399 context key means nothing when the SII module is not installed.
400 """
401 self.ensure_one()
! 402 credit_note = self.with_context(sii_refund_type="I")._reverse_moves(
403 default_values_list=[
404 {
405 "date": date,
406 "invoice_date": date,Lines 578-588 578 def _is_in_locked_period(self):
579 """Read from the company, not from ``_get_user_fiscal_lock_date``: the
580 cron runs as a user that only sees the fiscal year lock, and the
581 closed month is locked through ``period_lock_date``."""
! 582 self.ensure_one()
! 583 lock_date = max(
584 self.company_id.period_lock_date or date.min,
585 self.company_id.fiscalyear_lock_date or date.min,
586 )
! 587 return self.date <= lock_date |
…it notes _rectify_downpayment reversed the down payment without sii_refund_type in the context. The field is a stored computed field that can be edited, so the reversal copies the down payment's empty value onto the credit note and the compute never runs: the AEAT rejects those credit notes with "Valor o tipo incorrecto del campo: TipoRectificativa". Pass sii_refund_type='I' through the context, as the other callers of _reverse_moves do. Every credit note of the module (transfer, apply, refund and cancellation) goes through this method.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
pms_autoinvoice
The automatic invoice deducts the folio's down payments from the invoice of their customer. That step had several defects:
folio.sale.line._prepare_invoice_line()maps the taxes through the invoice fiscal position and has no default for it. The call did not pass it, so every automatic invoice with a down payment of its own customer failed withAttributeError: 'NoneType' object has no attribute 'map_tax'. The cron does not retry a folio once its day has passed, so those folios were left uninvoiced.delay_postthe negative draft was committed, and its posting job then failed, leaving a draft that cannot be posted.Now each down payment is deducted at most once per run, from the largest invoice of its customer that it fits in without going negative. It is left to the final invoice when its customer was moved to a host, and when it fits nowhere. In that last case the folio gets a message.
pms_downpayment_lifecycle
pms_autoinvoicereverses the down payments that the final invoice did not deduct withcancel=True, which unreconciles them from their payment. For a down payment of a locked period, that rewrites the customer balances of a month that is already closed.The
_reverse_downpayment_invoiceshook, added for exactly this case, now leaves those down payments to the transfer. The transfer settles them when the final invoice is posted, with a credit note issued withcancel=Falseand a transfer entry, so the closed period keeps its reconciliations. Down payments of an open period are reversed as before.The module's credit notes also came out without an SII refund type.
_rectify_downpaymentreversed the down payment with nosii_refund_typein the context. The field is a stored computed field that can be edited, so the reversal copies the down payment's empty value and the compute never runs, and the AEAT rejects the credit note (TipoRectificativa). It now passessii_refund_type='I'through the context, as the other callers of_reverse_movesalready do. This covers every credit note of the module, because they all go through this method.Tests
pms_autoinvoice:map_taxcase);pms_downpayment_lifecycle:These are accounting tests, so CI skips them when no chart of accounts is available. They were run locally on a fresh database with
l10n_generic_coa: 20 tests inpms_autoinvoiceand 43 inpms_downpayment_lifecycle, all passing. As a negative control, the new tests were also run against the code without this change: they fail with the originalmap_taxerror, the locked-period down payment gets reversed, and the credit note gets no SII refund type.