addons-cm/pos_account_payment_order_sepa/data/server_action.xml
2026-06-26 19:42:45 +02:00

133 lines
4.6 KiB
XML

<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="action_pos_collect_sepa" model="ir.actions.server">
<field name="name">Collect by SEPA direct debit</field>
<field name="model_id" ref="point_of_sale.model_pos_order" />
<field name="binding_model_id" ref="point_of_sale.model_pos_order" />
<field name="binding_view_types">list,form</field>
<field name="state">code</field>
<field name="code"><![CDATA[
# Collect the still-open "Customer Account" receivables generated by the
# selected POS orders through a SEPA direct debit payment order (debit
# order). For each order we take the receivable journal item of its session
# move, keep the unreconciled ones whose partner has a valid SEPA mandate,
# and create the payment lines inside a single draft inbound payment order.
# The SEPA XML (pain.008) is generated afterwards from that order by the user.
#
# Orders are skipped (and reported) when: no accounting move yet, no open
# receivable line, no partner, the line is already in a payment order, or the
# partner has no valid SEPA mandate. Refunds/credit lines are ignored.
Mode = env["account.payment.mode"]
Order = env["account.payment.order"]
Mandate = env["account.banking.mandate"]
# --- Resolve the inbound SEPA payment mode (partner mode, with fallback) ---
def sepa_mode_for(partner):
mode = partner.customer_payment_mode_id
if (
mode
and mode.payment_order_ok
and mode.payment_method_id.code == "sepa_direct_debit"
and mode.payment_method_id.payment_type == "inbound"
):
return mode
return False
fallback_mode = Mode.search(
[
("payment_order_ok", "=", True),
("payment_method_id.code", "=", "sepa_direct_debit"),
("payment_method_id.payment_type", "=", "inbound"),
],
limit=1,
)
# --- Classify the selected orders ---
lines_by_mode = {}
skipped = {"no_move": 0, "no_line": 0, "no_partner": 0, "already": 0, "no_mandate": 0, "no_mode": 0}
for order in records:
move = order.account_move
if not move:
skipped["no_move"] += 1
continue
rec_lines = move.line_ids.filtered(
lambda l: l.account_id.account_type == "asset_receivable"
and not l.reconciled
and l.amount_residual > 0
)
if not rec_lines:
skipped["no_line"] += 1
continue
for line in rec_lines:
partner = line.partner_id
if not partner:
skipped["no_partner"] += 1
continue
if env["account.payment.line"].search_count(
[("move_line_id", "=", line.id), ("order_id.state", "!=", "cancel")]
):
skipped["already"] += 1
continue
if not Mandate.search_count(
[("partner_id", "=", partner.commercial_partner_id.id), ("state", "=", "valid")]
):
skipped["no_mandate"] += 1
continue
mode = sepa_mode_for(partner) or fallback_mode
if not mode:
skipped["no_mode"] += 1
continue
lines_by_mode.setdefault(mode.id, env["account.move.line"])
lines_by_mode[mode.id] |= line
# --- Create one draft payment order per payment mode and add the lines ---
created_lines = 0
order_names = []
for mode_id, move_lines in lines_by_mode.items():
mode = Mode.browse(mode_id)
payment_order = Order.search(
[("payment_mode_id", "=", mode_id), ("state", "=", "draft")], limit=1
)
if not payment_order:
payment_order = Order.create({"payment_mode_id": mode_id})
move_lines.create_payment_line_from_move_line(payment_order)
created_lines += len(move_lines)
order_names.append(payment_order.name or "Draft")
# --- Report ---
message = (
"Payment lines created: %(created)s\n"
"Payment order(s): %(orders)s\n\n"
"Skipped:\n"
" no accounting move: %(no_move)s\n"
" no open receivable line: %(no_line)s\n"
" no partner: %(no_partner)s\n"
" already in a payment order: %(already)s\n"
" no valid SEPA mandate: %(no_mandate)s\n"
" no SEPA payment mode: %(no_mode)s"
) % {
"created": created_lines,
"orders": ", ".join(order_names) or "-",
"no_move": skipped["no_move"],
"no_line": skipped["no_line"],
"no_partner": skipped["no_partner"],
"already": skipped["already"],
"no_mandate": skipped["no_mandate"],
"no_mode": skipped["no_mode"],
}
action = {
"type": "ir.actions.client",
"tag": "display_notification",
"params": {
"title": "SEPA collection",
"message": message,
"type": "success" if created_lines else "warning",
"sticky": True,
},
}
]]></field>
</record>
</odoo>