odoo-addon-l10n-it-riba-oca 18.0.1.0.0.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. odoo/addons/l10n_it_riba_oca/README.rst +175 -0
  2. odoo/addons/l10n_it_riba_oca/__init__.py +9 -0
  3. odoo/addons/l10n_it_riba_oca/__manifest__.py +59 -0
  4. odoo/addons/l10n_it_riba_oca/data/riba_sequence.xml +14 -0
  5. odoo/addons/l10n_it_riba_oca/demo/riba_demo.xml +39 -0
  6. odoo/addons/l10n_it_riba_oca/hooks.py +17 -0
  7. odoo/addons/l10n_it_riba_oca/i18n/it.po +1686 -0
  8. odoo/addons/l10n_it_riba_oca/i18n/l10n_it_riba.pot +1573 -0
  9. odoo/addons/l10n_it_riba_oca/i18n/l10n_it_riba_oca.pot +1554 -0
  10. odoo/addons/l10n_it_riba_oca/migrations/18.0.1.0.0/pre-migrate.py +16 -0
  11. odoo/addons/l10n_it_riba_oca/models/__init__.py +14 -0
  12. odoo/addons/l10n_it_riba_oca/models/account.py +563 -0
  13. odoo/addons/l10n_it_riba_oca/models/account_config.py +35 -0
  14. odoo/addons/l10n_it_riba_oca/models/ir_ui_menu.py +27 -0
  15. odoo/addons/l10n_it_riba_oca/models/partner.py +35 -0
  16. odoo/addons/l10n_it_riba_oca/models/riba.py +560 -0
  17. odoo/addons/l10n_it_riba_oca/models/riba_config.py +120 -0
  18. odoo/addons/l10n_it_riba_oca/readme/CONFIGURE.md +33 -0
  19. odoo/addons/l10n_it_riba_oca/readme/CONTRIBUTORS.md +17 -0
  20. odoo/addons/l10n_it_riba_oca/readme/DESCRIPTION.md +3 -0
  21. odoo/addons/l10n_it_riba_oca/readme/USAGE.md +35 -0
  22. odoo/addons/l10n_it_riba_oca/report/__init__.py +4 -0
  23. odoo/addons/l10n_it_riba_oca/report/report.xml +16 -0
  24. odoo/addons/l10n_it_riba_oca/report/slip_qweb.py +18 -0
  25. odoo/addons/l10n_it_riba_oca/security/ir.model.access.csv +23 -0
  26. odoo/addons/l10n_it_riba_oca/security/riba_security.xml +34 -0
  27. odoo/addons/l10n_it_riba_oca/static/description/icon.png +0 -0
  28. odoo/addons/l10n_it_riba_oca/static/description/index.html +512 -0
  29. odoo/addons/l10n_it_riba_oca/tests/__init__.py +10 -0
  30. odoo/addons/l10n_it_riba_oca/tests/riba_common.py +339 -0
  31. odoo/addons/l10n_it_riba_oca/tests/test_account_move.py +54 -0
  32. odoo/addons/l10n_it_riba_oca/tests/test_menu.py +51 -0
  33. odoo/addons/l10n_it_riba_oca/tests/test_riba.py +905 -0
  34. odoo/addons/l10n_it_riba_oca/views/account_config_view.xml +46 -0
  35. odoo/addons/l10n_it_riba_oca/views/account_view.xml +205 -0
  36. odoo/addons/l10n_it_riba_oca/views/configuration_view.xml +94 -0
  37. odoo/addons/l10n_it_riba_oca/views/partner_view.xml +29 -0
  38. odoo/addons/l10n_it_riba_oca/views/riba_detail_view.xml +97 -0
  39. odoo/addons/l10n_it_riba_oca/views/riba_view.xml +296 -0
  40. odoo/addons/l10n_it_riba_oca/views/slip_report.xml +149 -0
  41. odoo/addons/l10n_it_riba_oca/views/wizard_credit.xml +46 -0
  42. odoo/addons/l10n_it_riba_oca/views/wizard_due_date_settlement.xml +37 -0
  43. odoo/addons/l10n_it_riba_oca/views/wizard_past_due.xml +67 -0
  44. odoo/addons/l10n_it_riba_oca/views/wizard_presentation.xml +48 -0
  45. odoo/addons/l10n_it_riba_oca/views/wizard_riba_file_export.xml +33 -0
  46. odoo/addons/l10n_it_riba_oca/views/wizard_riba_issue.xml +46 -0
  47. odoo/addons/l10n_it_riba_oca/views/wizard_riba_payment_date.xml +45 -0
  48. odoo/addons/l10n_it_riba_oca/wizard/__init__.py +16 -0
  49. odoo/addons/l10n_it_riba_oca/wizard/wizard_credit.py +280 -0
  50. odoo/addons/l10n_it_riba_oca/wizard/wizard_due_date_settlement.py +24 -0
  51. odoo/addons/l10n_it_riba_oca/wizard/wizard_past_due.py +319 -0
  52. odoo/addons/l10n_it_riba_oca/wizard/wizard_presentation_riba.py +47 -0
  53. odoo/addons/l10n_it_riba_oca/wizard/wizard_riba_file_export.py +429 -0
  54. odoo/addons/l10n_it_riba_oca/wizard/wizard_riba_issue.py +143 -0
  55. odoo/addons/l10n_it_riba_oca/wizard/wizard_riba_multiple_payment.py +107 -0
  56. odoo/addons/l10n_it_riba_oca/wizard/wizard_riba_multiple_payment_views.xml +46 -0
  57. odoo/addons/l10n_it_riba_oca/wizard/wizard_riba_payment_date.py +31 -0
  58. odoo_addon_l10n_it_riba_oca-18.0.1.0.0.8.dist-info/METADATA +197 -0
  59. odoo_addon_l10n_it_riba_oca-18.0.1.0.0.8.dist-info/RECORD +61 -0
  60. odoo_addon_l10n_it_riba_oca-18.0.1.0.0.8.dist-info/WHEEL +5 -0
  61. odoo_addon_l10n_it_riba_oca-18.0.1.0.0.8.dist-info/top_level.txt +1 -0
@@ -0,0 +1,560 @@
1
+ # Copyright (C) 2012 Andrea Cometa.
2
+ # Email: info@andreacometa.it
3
+ # Web site: http://www.andreacometa.it
4
+ # Copyright (C) 2012 Associazione OpenERP Italia
5
+ # (<http://www.odoo-italia.org>).
6
+ # Copyright (C) 2012-2017 Lorenzo Battistini - Agile Business Group
7
+ # Copyright 2023 Simone Rubino - Aion Tech
8
+ # Copyright 2024 Nextev Srl
9
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
10
+
11
+
12
+ from odoo import api, fields, models
13
+ from odoo.exceptions import UserError
14
+
15
+
16
+ class RibaList(models.Model):
17
+ def _compute_acceptance_move_ids(self):
18
+ for riba in self:
19
+ move_ids = self.env["account.move"]
20
+ for line in riba.line_ids:
21
+ move_ids |= line.acceptance_move_id
22
+ riba.acceptance_move_ids = move_ids
23
+
24
+ def _compute_past_due_move_ids(self):
25
+ for riba in self:
26
+ move_ids = self.env["account.move"]
27
+ for line in riba.line_ids:
28
+ move_ids |= line.past_due_move_id
29
+ riba.past_due_move_ids = move_ids
30
+
31
+ def _compute_payment_ids(self):
32
+ """
33
+ Compute payment-related move lines for RiBa slips.
34
+
35
+ The computed field helps track payment status and provides visibility
36
+ into which specific accounting entries are related to each RiBa slip.
37
+
38
+ Migration Note (Odoo 18.0):
39
+ This implementation uses a direct search on slip_line_id field, which is
40
+ more efficient and reliable than the previous complex logic that tried
41
+ to match payments through reconciliation records. The slip_line_id field
42
+ is explicitly set during settlement creation, ensuring accurate tracking.
43
+ """
44
+ for riba in self:
45
+ # Find all move lines that reference this RiBa's slip lines
46
+ # These are created during settlement and represent actual payments
47
+ payment_lines = self.env["account.move.line"].search(
48
+ [("slip_line_id", "in", riba.line_ids.ids)]
49
+ )
50
+ riba.payment_ids = payment_lines
51
+
52
+ def _compute_total_amount(self):
53
+ for riba in self:
54
+ riba.total_amount = 0.0
55
+ for line in riba.line_ids:
56
+ riba.total_amount += line.amount
57
+
58
+ _name = "riba.slip"
59
+ _description = "RiBa Slip"
60
+ _inherit = ["mail.thread"]
61
+ _order = "date_created desc"
62
+
63
+ name = fields.Char(
64
+ "Reference",
65
+ required=True,
66
+ default=(lambda self: self.env["ir.sequence"].next_by_code("riba.slip")),
67
+ )
68
+ config_id = fields.Many2one(
69
+ "riba.configuration",
70
+ string="Configuration",
71
+ index=True,
72
+ help="RiBa configuration to be used.",
73
+ )
74
+ state = fields.Selection(
75
+ [
76
+ ("draft", "Draft"),
77
+ ("accepted", "Accepted"),
78
+ ("credited", "Credited"),
79
+ ("paid", "Paid"),
80
+ ("past_due", "Past Due"),
81
+ ("cancel", "Canceled"),
82
+ ],
83
+ readonly=True,
84
+ default="draft",
85
+ )
86
+ line_ids = fields.One2many(
87
+ "riba.slip.line",
88
+ "slip_id",
89
+ "RiBa Due Dates",
90
+ )
91
+ user_id = fields.Many2one(
92
+ "res.users",
93
+ "User",
94
+ required=True,
95
+ default=lambda self: self.env.user,
96
+ )
97
+ date_created = fields.Date(
98
+ "Creation Date",
99
+ readonly=True,
100
+ default=lambda self: fields.Date.context_today(self),
101
+ )
102
+ date_accepted = fields.Date("Acceptance Date")
103
+ date_credited = fields.Date("Credit Date")
104
+ date_paid = fields.Date(
105
+ string="Payment Date",
106
+ help="Default date for payments.",
107
+ )
108
+ date_past_due = fields.Date("Past Due Date", readonly=True)
109
+ company_id = fields.Many2one(
110
+ "res.company",
111
+ "Company",
112
+ required=True,
113
+ default=lambda self: self.env.company,
114
+ )
115
+ acceptance_move_ids = fields.Many2many(
116
+ "account.move",
117
+ compute="_compute_acceptance_move_ids",
118
+ string="Acceptance Entries",
119
+ )
120
+ credit_move_id = fields.Many2one("account.move", "Credit Entry", readonly=True)
121
+ payment_ids = fields.Many2many(
122
+ "account.move.line", compute="_compute_payment_ids", string="Payments"
123
+ )
124
+ past_due_move_ids = fields.Many2many(
125
+ "account.move", compute="_compute_past_due_move_ids", string="Past Due Entries"
126
+ )
127
+ type = fields.Selection(string="Type", related="config_id.type", readonly=True)
128
+ registration_date = fields.Date(
129
+ required=True,
130
+ default=lambda self: fields.Date.context_today(self),
131
+ help="Keep empty to use the current date.",
132
+ )
133
+ total_amount = fields.Float(
134
+ string="Amount",
135
+ compute="_compute_total_amount",
136
+ )
137
+
138
+ def action_riba_export(self):
139
+ return {
140
+ "type": "ir.actions.act_window",
141
+ "name": "Issue RiBa",
142
+ "res_model": "riba.file.export",
143
+ "view_mode": "form",
144
+ "target": "new",
145
+ "context": self.env.context,
146
+ }
147
+
148
+ def action_riba_due_date_settlement(self):
149
+ return {
150
+ "type": "ir.actions.act_window",
151
+ "name": "C/O Due Date Settlement",
152
+ "res_model": "riba.due.date.settlement",
153
+ "view_mode": "form",
154
+ "target": "new",
155
+ "context": self.env.context,
156
+ }
157
+
158
+ @api.ondelete(at_uninstall=False)
159
+ def _unlink_if_not_confirmed(self):
160
+ for riba_list in self:
161
+ if riba_list.state not in ("draft", "cancel"):
162
+ raise UserError(
163
+ self.env._(
164
+ "Slip %(name)s is in state '%(state)s'."
165
+ " You can only delete documents"
166
+ " in state 'Draft' or 'Canceled'.",
167
+ name=riba_list.name,
168
+ state=riba_list.state,
169
+ )
170
+ )
171
+
172
+ def confirm(self):
173
+ for slip in self:
174
+ for line in slip.line_ids:
175
+ line.confirm()
176
+
177
+ def riba_cancel(self):
178
+ for slip in self:
179
+ for line in slip.line_ids:
180
+ line.state = "cancel"
181
+ if line.acceptance_move_id:
182
+ line.acceptance_move_id.unlink()
183
+ if line.past_due_move_id:
184
+ line.past_due_move_id.unlink()
185
+ if slip.credit_move_id:
186
+ slip.credit_move_id.unlink()
187
+ slip.state = "cancel"
188
+
189
+ def settle_all_line(self):
190
+ payment_wizard_action = (
191
+ self.env["riba.payment.multiple"]
192
+ .with_context(
193
+ active_ids=self.ids,
194
+ )
195
+ .get_formview_action()
196
+ )
197
+ payment_wizard_action.update(
198
+ name=self.env._("Settle lines"),
199
+ target="new",
200
+ )
201
+ return payment_wizard_action
202
+
203
+ @api.onchange("date_accepted", "date_credited")
204
+ def _onchange_date(self):
205
+ if self.date_accepted and self.date_credited:
206
+ if self.date_accepted > self.date_credited:
207
+ raise UserError(
208
+ self.env._(
209
+ "Credit date must be greater or equal to acceptance date."
210
+ )
211
+ )
212
+
213
+ def riba_past_due(self):
214
+ self.state = "past_due"
215
+ self.date_past_due = fields.Date.context_today(self)
216
+
217
+ def test_state(self, state):
218
+ for riba_list in self:
219
+ for line in riba_list.line_ids:
220
+ if line.state != state:
221
+ return False
222
+ return True
223
+
224
+ def test_accepted(self):
225
+ return self.test_state("confirmed")
226
+
227
+ def test_past_due(self):
228
+ return self.test_state("past_due")
229
+
230
+ def test_paid(self):
231
+ return self.test_state("paid")
232
+
233
+ def action_cancel_draft(self):
234
+ for riba_list in self:
235
+ riba_list.state = "draft"
236
+ for line in riba_list.line_ids:
237
+ line.state = "draft"
238
+
239
+ def action_open_lines(self):
240
+ action = self.env.ref("l10n_it_riba_oca.detail_riba_action").read()[0]
241
+ action["domain"] = [("slip_id", "=", self.id)]
242
+ return action
243
+
244
+
245
+ class RibaListLine(models.Model):
246
+ _name = "riba.slip.line"
247
+ _inherit = "mail.thread"
248
+ _description = "RiBa Details"
249
+ _rec_name = "sequence"
250
+
251
+ def _compute_line_values(self):
252
+ for line in self:
253
+ line.amount = 0.0
254
+ line.invoice_date = ""
255
+ line.invoice_number = ""
256
+ for move_line in line.move_line_ids:
257
+ line.amount += move_line.amount
258
+ move_date = move_line.move_line_id.move_id.invoice_date
259
+ if move_date:
260
+ move_date = str(
261
+ fields.Date.from_string(move_date).strftime("%d/%m/%Y")
262
+ )
263
+ if not line.invoice_date:
264
+ line.invoice_date = move_date
265
+ else:
266
+ line.invoice_date = f"{line.invoice_date}, {move_date}"
267
+ if not line.invoice_number:
268
+ line.invoice_number = str(
269
+ move_line.move_line_id.move_id.name
270
+ if move_line.move_line_id.move_id.display_name == "/"
271
+ else move_line.move_line_id.move_id.display_name
272
+ )
273
+ else:
274
+ line.invoice_number = "{}, {}".format(
275
+ line.invoice_number,
276
+ str(
277
+ move_line.move_line_id.move_id.name
278
+ if move_line.move_line_id.move_id.display_name == "/"
279
+ else move_line.move_line_id.move_id.display_name
280
+ ),
281
+ )
282
+
283
+ amount = fields.Float(compute="_compute_line_values")
284
+ invoice_date = fields.Char(compute="_compute_line_values", size=256)
285
+ invoice_number = fields.Char(compute="_compute_line_values", size=256)
286
+ cig = fields.Char(string="CIG", size=256)
287
+ cup = fields.Char(string="CUP", size=256)
288
+
289
+ sequence = fields.Integer("Number")
290
+ move_line_ids = fields.One2many(
291
+ "riba.slip.move.line", "riba_line_id", string="Credit Move Lines"
292
+ )
293
+ acceptance_move_id = fields.Many2one(
294
+ "account.move", string="Acceptance Entry", readonly=True
295
+ )
296
+ credit_move_id = fields.Many2one(
297
+ "account.move", string="Credit Entry", readonly=True
298
+ )
299
+ past_due_move_id = fields.Many2one(
300
+ "account.move", string="Past Due Entry", readonly=True
301
+ )
302
+ acceptance_account_id = fields.Many2one(
303
+ "account.account", string="Acceptance Account"
304
+ )
305
+ bank_id = fields.Many2one("res.partner.bank", string="Debtor Bank")
306
+ iban = fields.Char(
307
+ related="bank_id.acc_number", string="IBAN", store=False, readonly=True
308
+ )
309
+ slip_id = fields.Many2one(
310
+ "riba.slip", string="Slip", required=True, ondelete="cascade"
311
+ )
312
+ partner_id = fields.Many2one("res.partner", string="Customer", readonly=True)
313
+ due_date = fields.Date(readonly=True)
314
+ state = fields.Selection(
315
+ [
316
+ ("draft", "Draft"),
317
+ ("confirmed", "Confirmed"),
318
+ ("credited", "Credited"),
319
+ ("paid", "Paid"),
320
+ ("past_due", "Past Due"),
321
+ ("cancel", "Canceled"),
322
+ ],
323
+ readonly=True,
324
+ tracking=True,
325
+ )
326
+ payment_id = fields.Many2one("account.move.line", string="Payments", readonly=True)
327
+ type = fields.Selection(
328
+ string="Type", related="slip_id.config_id.type", readonly=True
329
+ )
330
+ config_id = fields.Many2one(
331
+ string="Configuration", related="slip_id.config_id", readonly=True
332
+ )
333
+ company_id = fields.Many2one(
334
+ "res.company",
335
+ string="Company",
336
+ related="slip_id.company_id",
337
+ store=True,
338
+ readonly=True,
339
+ related_sudo=False,
340
+ )
341
+
342
+ def confirm(self):
343
+ move_model = self.env["account.move"]
344
+ move_line_model = self.env["account.move.line"]
345
+ for line in self:
346
+ journal = line.slip_id.config_id.acceptance_journal_id
347
+ total_credit = 0.0
348
+ move = move_model.create(
349
+ {
350
+ "ref": f"{line.invoice_number} RiBa {line.slip_id.name} \
351
+ - Line {line.sequence}",
352
+ "journal_id": journal.id,
353
+ "date": line.due_date,
354
+ }
355
+ )
356
+ to_be_reconciled = self.env["account.move.line"]
357
+ riba_move_line_name = ""
358
+ for riba_move_line in line.move_line_ids:
359
+ total_credit += riba_move_line.amount
360
+ if (
361
+ str(riba_move_line.move_line_id.move_id.sequence_number)
362
+ and str(riba_move_line.move_line_id.move_id.sequence_number)
363
+ not in riba_move_line_name
364
+ ):
365
+ riba_move_line_name = " ".join(
366
+ [
367
+ riba_move_line_name,
368
+ str(riba_move_line.move_line_id.move_id.sequence_number),
369
+ ]
370
+ ).lstrip()
371
+ elif (
372
+ riba_move_line.move_line_id.name
373
+ and riba_move_line.move_line_id.name not in riba_move_line_name
374
+ ):
375
+ riba_move_line_name = " ".join(
376
+ [riba_move_line_name, riba_move_line.move_line_id.name]
377
+ ).lstrip()
378
+ move_line = move_line_model.with_context(
379
+ check_move_validity=False
380
+ ).create(
381
+ {
382
+ "name": (
383
+ riba_move_line.move_line_id.move_id
384
+ and riba_move_line.move_line_id.move_id.sequence_number
385
+ or riba_move_line.move_line_id.name
386
+ ),
387
+ "partner_id": line.partner_id.id,
388
+ "account_id": riba_move_line.move_line_id.account_id.id,
389
+ "credit": riba_move_line.amount,
390
+ "debit": 0.0,
391
+ "move_id": move.id,
392
+ }
393
+ )
394
+ to_be_reconciled |= move_line
395
+ to_be_reconciled |= riba_move_line.move_line_id
396
+ move_line_model.with_context(check_move_validity=False).create(
397
+ {
398
+ "name": f"{line.invoice_number} RiBa {line.slip_id.name}-\
399
+ {line.sequence} Ref. {riba_move_line_name} - \
400
+ {line.partner_id.name}",
401
+ "account_id": (
402
+ line.acceptance_account_id.id
403
+ or line.slip_id.config_id.acceptance_account_id.id
404
+ # in questo modo se la riga non ha conto accettazione
405
+ # viene prelevato il conto in configuration riba
406
+ ),
407
+ "partner_id": line.partner_id.id,
408
+ "date_maturity": line.due_date,
409
+ "credit": 0.0,
410
+ "debit": total_credit,
411
+ "move_id": move.id,
412
+ }
413
+ )
414
+ move.action_post()
415
+ to_be_reconciled.reconcile()
416
+ line.write(
417
+ {
418
+ "acceptance_move_id": move.id,
419
+ "state": "confirmed",
420
+ }
421
+ )
422
+ line.slip_id.state = "accepted"
423
+ if not line.slip_id.date_accepted:
424
+ line.slip_id.date_accepted = fields.Date.context_today(self)
425
+
426
+ def button_settle(self):
427
+ payment_wizard_action = (
428
+ self.env["riba.payment.multiple"]
429
+ .with_context(
430
+ active_ids=self.slip_id.ids,
431
+ default_riba_line_ids=self.ids,
432
+ )
433
+ .get_formview_action()
434
+ )
435
+ payment_wizard_action.update(
436
+ name=self.env._("Settle line"),
437
+ target="new",
438
+ )
439
+ return payment_wizard_action
440
+
441
+ def riba_line_settlement(self, date=None):
442
+ """
443
+ Create settlement payment entries for RiBa lines.
444
+
445
+ This method handles the final settlement of RiBa lines by creating
446
+ accounting entries that record the actual payment received from customers
447
+ through the bank. It reconciles the credit account with the bank account
448
+ to complete the RiBa collection process.
449
+
450
+ Settlement Process:
451
+ 1. Find the original credit move line that needs to be settled
452
+ 2. Calculate the settlement amount (considering partial payments for SBF)
453
+ 3. Create settlement move with:
454
+ - Credit line: reduces the RiBa credit account
455
+ - Debit line: increases the bank account
456
+ 4. Reconcile all related move lines to close the collection cycle
457
+
458
+ :param date: The settlement date. If not provided, uses today's date.
459
+ :type date: date or None
460
+ :raises UserError: If settlement journal is not configured
461
+
462
+ Business Flow:
463
+ - Customer pays invoice → Bank receives payment → Bank notifies company
464
+ - Company records settlement → RiBa line moves to 'paid' state
465
+ """
466
+ # Validate configuration before proceeding
467
+ if not self.slip_id.config_id.settlement_journal_id:
468
+ raise UserError(self.env._("Please define a Settlement Journal."))
469
+
470
+ # Initialize models for creating accounting entries
471
+ move_model = self.env["account.move"]
472
+ move_line_model = self.env["account.move.line"]
473
+
474
+ # Find the original credit move line that needs to be settled
475
+ # This represents the amount that was credited when the RiBa was issued
476
+ settlement_move_line = move_line_model.search(
477
+ [
478
+ ("account_id", "=", self.slip_id.config_id.credit_account_id.id),
479
+ ("move_id", "=", self.credit_move_id.id),
480
+ ("debit", "!=", 0), # We need the debit side of the credit entry
481
+ ]
482
+ )
483
+
484
+ # Reduce settlement amount by amounts already matched (partial payments)
485
+ settlement_move_amount = settlement_move_line.debit - sum(
486
+ settlement_move_line.mapped("matched_credit_ids.amount")
487
+ )
488
+
489
+ # Prepare settlement move data
490
+ move_ref = f"Settlement RiBa {self.slip_id.name}"
491
+ move_date = date or fields.Date.context_today(self)
492
+
493
+ # Create the settlement accounting move
494
+ settlement_move = move_model.create(
495
+ {
496
+ "journal_id": (self.slip_id.config_id.settlement_journal_id.id),
497
+ "date": move_date,
498
+ "ref": move_ref,
499
+ }
500
+ )
501
+
502
+ # Create credit move lines for each RiBa line being settled
503
+ move_lines_credit = self.env["account.move.line"]
504
+ for riba_line in self:
505
+ # Credit the RiBa account to reduce the outstanding amount
506
+ move_line = move_line_model.with_context(check_move_validity=False).create(
507
+ {
508
+ "name": move_ref,
509
+ "partner_id": riba_line.partner_id.id,
510
+ "account_id": riba_line.slip_id.config_id.credit_account_id.id,
511
+ "credit": riba_line.amount, # Reduce RiBa credit account
512
+ "debit": 0.0,
513
+ "move_id": settlement_move.id,
514
+ "slip_line_id": riba_line.id, # Link back to RiBa line
515
+ }
516
+ )
517
+ move_lines_credit |= move_line
518
+
519
+ # Create the corresponding bank debit entry
520
+ bank_account = riba_line.slip_id.config_id.bank_account_id
521
+ move_line_model.with_context(check_move_validity=False).create(
522
+ {
523
+ "name": move_ref,
524
+ "account_id": bank_account.id,
525
+ "credit": 0.0,
526
+ "debit": settlement_move_amount, # Increase bank account
527
+ "move_id": settlement_move.id,
528
+ }
529
+ )
530
+
531
+ # Post the settlement move
532
+ move_lines_credit.mapped("move_id").action_post()
533
+
534
+ # Prepare reconciliation: collect all move lines that need to be reconciled
535
+ to_be_settled = self.env["account.move.line"]
536
+ for move_line in move_lines_credit:
537
+ to_be_settled |= move_line
538
+ # Add the original credit move line
539
+ to_be_settled |= settlement_move_line
540
+
541
+ # Reconcile all related move lines to complete the settlement
542
+ # This closes the accounting cycle for this RiBa collection
543
+ to_be_settled.reconcile()
544
+
545
+ def settle_riba_line(self):
546
+ for line in self:
547
+ if line.state == "credited":
548
+ line.riba_line_settlement()
549
+
550
+
551
+ class RibaListMoveLine(models.Model):
552
+ _name = "riba.slip.move.line"
553
+ _description = "RiBa Details"
554
+ _rec_name = "amount"
555
+
556
+ amount = fields.Float(digits="Account")
557
+ move_line_id = fields.Many2one("account.move.line", string="Credit Move Line")
558
+ riba_line_id = fields.Many2one(
559
+ "riba.slip.line", string="Slip Line", ondelete="cascade"
560
+ )
@@ -0,0 +1,120 @@
1
+ # Copyright (C) 2012 Andrea Cometa.
2
+ # Email: info@andreacometa.it
3
+ # Web site: http://www.andreacometa.it
4
+ # Copyright (C) 2012 Associazione OpenERP Italia
5
+ # (<http://www.odoo-italia.org>).
6
+ # Copyright (C) 2012-2017 Lorenzo Battistini - Agile Business Group
7
+ # Copyright 2023 Simone Rubino - Aion Tech
8
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
9
+
10
+ from odoo import fields, models
11
+
12
+
13
+ class RibaConfiguration(models.Model):
14
+ _name = "riba.configuration"
15
+ _description = "Configuration parameters for RiBa"
16
+ _check_company_auto = True
17
+
18
+ name = fields.Char("Description", size=64, required=True)
19
+ type = fields.Selection(
20
+ [("sbf", "Subject To Collection"), ("incasso", "After Collection")],
21
+ "Issue Mode",
22
+ required=True,
23
+ )
24
+ bank_id = fields.Many2one(
25
+ "res.partner.bank",
26
+ "Bank Account",
27
+ required=True,
28
+ check_company=True,
29
+ help="Bank account used for RiBa issuing.",
30
+ )
31
+ acceptance_journal_id = fields.Many2one(
32
+ "account.journal",
33
+ "Acceptance Journal",
34
+ required=True,
35
+ check_company=True,
36
+ help="Journal used when RiBa is accepted by the bank.",
37
+ )
38
+ acceptance_account_id = fields.Many2one(
39
+ "account.account",
40
+ "Acceptance Account",
41
+ required=True,
42
+ check_company=True,
43
+ help="Account used when RiBa is accepted by the bank.",
44
+ )
45
+ company_id = fields.Many2one(
46
+ "res.company",
47
+ "Company",
48
+ required=True,
49
+ default=lambda self: self.env.company,
50
+ )
51
+ credit_journal_id = fields.Many2one(
52
+ "account.journal",
53
+ "Credit Journal",
54
+ check_company=True,
55
+ help="Journal used when RiBa amount is credited by the bank.",
56
+ )
57
+ credit_account_id = fields.Many2one(
58
+ "account.account",
59
+ "RiBa Account",
60
+ check_company=True,
61
+ help="Account used when RiBa amount is credited by the bank.",
62
+ domain="[('account_type', '!=', 'liability_credit_card')]",
63
+ )
64
+ bank_account_id = fields.Many2one(
65
+ "account.account",
66
+ "A/C Bank Account",
67
+ check_company=True,
68
+ )
69
+ bank_expense_account_id = fields.Many2one("account.account", "Bank Fees Account")
70
+ past_due_journal_id = fields.Many2one(
71
+ "account.journal",
72
+ "Past Due Journal",
73
+ check_company=True,
74
+ help="Journal used when RiBa is past due.",
75
+ )
76
+ overdue_credit_account_id = fields.Many2one(
77
+ "account.account",
78
+ "Past Due Bills Account",
79
+ check_company=True,
80
+ )
81
+ past_due_fee_amount = fields.Float(
82
+ "Fee Amount",
83
+ )
84
+ protest_charge_account_id = fields.Many2one(
85
+ "account.account",
86
+ "Protest Fee Account",
87
+ check_company=True,
88
+ )
89
+ settlement_journal_id = fields.Many2one(
90
+ "account.journal",
91
+ "Settlement Journal",
92
+ check_company=True,
93
+ help="Journal used when customers finally pay the invoice to bank.",
94
+ )
95
+
96
+ def get_default_value_by_list(self, field_name):
97
+ if not self.env.context.get("active_id", False):
98
+ return False
99
+ ribalist_model = self.env["riba.slip"]
100
+ ribalist = ribalist_model.browse(self.env.context["active_id"])
101
+ return (
102
+ ribalist.config_id[field_name]
103
+ and ribalist.config_id[field_name].id
104
+ or False
105
+ )
106
+
107
+ def get_default_value_by_list_line(self, field_name):
108
+ if not self.env.context.get("active_id", False):
109
+ return False if field_name != "past_due_fee_amount" else 0.0
110
+ ribalist_line = self.env["riba.slip.line"].browse(self.env.context["active_id"])
111
+ res = ribalist_line.slip_id.config_id[field_name]
112
+ if field_name == "past_due_fee_amount":
113
+ return res
114
+ if res:
115
+ # we need to check if the field is string like "config_type" or
116
+ # many2one like journals and accounts
117
+ if isinstance(res, str):
118
+ return res
119
+ return res.id
120
+ return False