@@ -86,8 +86,8 @@ def _cron_recurring_interests_invoices(self):
86
86
try :
87
87
rec .create_interest_invoices ()
88
88
rec .env .cr .commit ()
89
- except :
90
- _logger .error ('Error creating interest invoices for company: %s' , rec .company_id .name )
89
+ except Exception as e :
90
+ _logger .error ('Error creating interest invoices for company: %s, %s ' , rec .company_id .name , str ( e ) )
91
91
companies_with_errors .append (rec .company_id .name )
92
92
rec .env .cr .rollback ()
93
93
@@ -96,6 +96,19 @@ def _cron_recurring_interests_invoices(self):
96
96
error_message = _ ("We couldn't run interest invoices cron job in the following companies: %s." ) % company_names
97
97
raise UserError (error_message )
98
98
99
+ def _calculate_date_deltas (self , rule_type , interval ):
100
+ """
101
+ Calcula los intervalos de fechas para la generación de intereses.
102
+ """
103
+ deltas = {
104
+ 'daily' : relativedelta (days = interval ),
105
+ 'weekly' : relativedelta (weeks = interval ),
106
+ 'monthly' : relativedelta (months = interval ),
107
+ 'yearly' : relativedelta (years = interval ),
108
+ }
109
+ return deltas .get (rule_type , relativedelta (months = interval ))
110
+
111
+
99
112
def create_interest_invoices (self ):
100
113
for rec in self :
101
114
_logger .info (
@@ -108,18 +121,8 @@ def create_interest_invoices(self):
108
121
rule_type = rec .rule_type
109
122
interval = rec .interval
110
123
111
- if rule_type == 'daily' :
112
- next_delta = relativedelta (days = + interval )
113
- from_date_delta = relativedelta (days = - interval )
114
- elif rule_type == 'weekly' :
115
- next_delta = relativedelta (weeks = + interval )
116
- from_date_delta = relativedelta (weeks = - interval )
117
- elif rule_type == 'monthly' :
118
- next_delta = relativedelta (months = + interval )
119
- from_date_delta = relativedelta (months = - interval )
120
- else :
121
- next_delta = relativedelta (years = + interval )
122
- from_date_delta = relativedelta (years = - interval )
124
+ next_delta = self ._calculate_date_deltas (rule_type , interval )
125
+ from_date_delta = self ._calculate_date_deltas (rule_type , - interval )
123
126
124
127
from_date = to_date + from_date_delta
125
128
@@ -140,44 +143,22 @@ def _get_move_line_domains(self):
140
143
]
141
144
return move_line_domain
142
145
143
- def create_invoices (self , from_date , to_date , groupby = [ 'partner_id' ] ):
146
+ def _update_deuda (self , deuda , partner , key , value ):
144
147
"""
145
- tengo deudas viejas por 2000 (super viejas)
146
- el 1 facturo 1000 que vencen el 20
147
- el 25 pagó 400.
148
- Detalle de cálculo de intereses:
149
- * interés por todo lo viejo (2000) x el rate
150
- * interés de todo lo que venció en el último período ($600) x días que estuvo vencido (10 días)
151
- * si además marcó "latest payment intereset" se agrega interés por los días que pagó tarde, es decir $400 x 5 días
148
+ Actualiza el diccionario de deuda para un partner específico.
149
+ Si el partner no existe en la deuda, lo inicializa.
150
+ Si la clave no existe para el partner, la agrega.
152
151
"""
153
- self .ensure_one ()
154
-
155
- journal = self .env ['account.journal' ].search ([
156
- ('type' , '=' , 'sale' ),
157
- ('company_id' , '=' , self .company_id .id )], limit = 1 )
152
+ if partner not in deuda :
153
+ deuda [partner ] = {}
154
+ deuda [partner ][key ] = deuda [partner ].get (key , 0 ) + value
158
155
159
- if self .receivable_account_ids != journal .default_account_id :
160
- journal = self .env ['account.journal' ].search ([('default_account_id' ,'in' ,self .receivable_account_ids .ids )], limit = 1 ) or journal
161
-
162
- # vemos todo lo impago que vencia antes del comienzo de este periodo de intereses ya que el interes ahí se calcula sobre el total
163
- # entre from date y to_date tenemos que calcular parciales segun dias cuando vencia comprobante
164
- move_line_domain_previous_periods = self ._get_move_line_domains () + [('full_reconcile_id' , '=' , False ), ('date_maturity' , '<' , from_date )]
165
- # Check if a filter is set
166
- if self .domain :
167
- move_line_domain_previous_periods += safe_eval (self .domain )
168
-
169
- # fields = ['id:recordset', 'amount_residual:sum', 'partner_id:recordset', 'account_id:recordset'] descarto esto porque ya no se estan usando la mayoria de valores
170
- fields = ['amount_residual:sum' ]
171
-
172
- move_line = self .env ['account.move.line' ]
173
- # DE ACA VAN A SALIR LAS LINES DE DEUDAS ANTERIORES
174
- previous_grouped_lines = move_line ._read_group (
175
- domain = move_line_domain_previous_periods ,
176
- groupby = groupby ,
177
- aggregates = fields ,
178
- )
179
-
180
- deuda = {x [0 ]: {'Deuda periodos anteriores' : x [1 ] * self .rate } for x in previous_grouped_lines }
156
+ def _calculate_debts (self , from_date , to_date , groupby = ['partner_id' ]):
157
+ """
158
+ Calcula las deudas e intereses por partner.
159
+ Retorna un diccionario estructurado con los cálculos.
160
+ """
161
+ deuda = {}
181
162
182
163
interest_rate = {
183
164
'daily' : 1 ,
@@ -186,66 +167,85 @@ def create_invoices(self, from_date, to_date, groupby=['partner_id']):
186
167
'yearly' : 360 ,
187
168
}
188
169
189
- # calculamos intereses de facturas del ultimo periodo
190
- last_period_lines = move_line .search (self ._get_move_line_domains () + [('amount_residual' , '>' , 0 ), ('date_maturity' , '>=' , from_date ), ('date_maturity' , '<' , to_date )])
191
- for partner , amls in last_period_lines .grouped ('partner_id' ).items ():
192
- interest = 0
193
- for move , lines in amls .grouped ('move_id' ).items ():
194
- days = (to_date - move .invoice_date_due ).days
195
- interest += move .amount_residual * days * (self .rate / interest_rate [self .rule_type ])
196
- if partner in deuda :
197
- deuda [partner ]['Deuda último periodo' ] = interest
198
- else :
199
- deuda [partner ] = {'Deuda último periodo' : interest }
170
+ # Deudas de períodos anteriores
171
+ previous_grouped_lines = self .env ['account.move.line' ]._read_group (
172
+ domain = self ._get_move_line_domains () + [('full_reconcile_id' , '=' , False ), ('date_maturity' , '<' , from_date )],
173
+ groupby = groupby ,
174
+ aggregates = ['amount_residual:sum' ],
175
+ )
176
+ for x in previous_grouped_lines :
177
+ self ._update_deuda (deuda , x [0 ], 'Deuda periodos anteriores' , x [1 ] * self .rate )
200
178
179
+ # Intereses por el último período
180
+ last_period_lines = self .env ['account.move.line' ].search (
181
+ self ._get_move_line_domains () + [('amount_residual' , '>' , 0 ), ('date_maturity' , '>=' , from_date ), ('date_maturity' , '<' , to_date )]
182
+ )
183
+ for partner , amls in last_period_lines .grouped ('partner_id' ).items ():
184
+ interest = sum (
185
+ move .amount_residual * ((to_date - move .invoice_date_due ).days - 1 ) * (self .rate / interest_rate [self .rule_type ])
186
+ for move , lines in amls .grouped ('move_id' ).items ()
187
+ )
188
+ self ._update_deuda (deuda , partner , 'Deuda último periodo' , interest )
201
189
202
- # Feature de intereses por pago tardio (periodo actual)
190
+ # Intereses por pagos tardíos
203
191
if self .late_payment_interest :
204
192
205
193
partials = self .env ['account.partial.reconcile' ].search ([
194
+ # lo dejamos para NTH
195
+ # debit_move_id. safe eval domain
206
196
('debit_move_id.partner_id.active' , '=' , True ),
207
197
('debit_move_id.date_maturity' , '>=' , from_date ),
208
198
('debit_move_id.date_maturity' , '<=' , to_date ),
209
199
('debit_move_id.parent_state' , '=' , 'posted' ),
210
- # lo dejamos para NTH
211
- # debit_move_id. safe eval domain
212
200
('debit_move_id.account_id' , 'in' , self .receivable_account_ids .ids ),
213
201
('credit_move_id.date' , '>=' , from_date ),
214
202
('credit_move_id.date' , '<' , to_date )]).grouped ('debit_move_id' )
215
203
216
204
for move_line , parts in partials .items ():
217
205
due_payments = parts .filtered (lambda x : x .credit_move_id .date > x .debit_move_id .date_maturity )
206
+ interest = 0
218
207
if due_payments :
219
208
due_payments_amount = sum (due_payments .mapped ('amount' ))
220
209
last_date_payment = parts .filtered (lambda x : x .credit_move_id .date > x .debit_move_id .date_maturity ).sorted ('max_date' )[- 1 ].max_date
221
210
days = (last_date_payment - move_line .date_maturity ).days
222
211
interest += due_payments_amount * days * (self .rate / interest_rate [self .rule_type ])
223
- partner = move_line .partner_id
224
- if partner in deuda and 'Deuda pagos vencidos' in deuda [partner ]:
225
- deuda [partner ]['Deuda pagos vencidos' ] += interest
226
- elif partner in deuda :
227
- deuda [partner ]['Deuda pagos vencidos' ] = interest
228
- else :
229
- deuda [partner ] = {'Deuda pagos vencidos' : interest }
230
-
231
- self = self .with_context (
232
- company_id = self .company_id .id ,
233
- mail_notrack = True ,
234
- prefetch_fields = False ).with_company (self .company_id )
212
+ self ._update_deuda (deuda , move_line .partner_id , 'Deuda pagos vencidos' , interest )
213
+
214
+ return deuda
215
+
216
+ def create_invoices (self , from_date , to_date ):
217
+ """
218
+ Crea facturas de intereses a cada partner basadas en los cálculos de deuda.
219
+ Ejemplo:
220
+ Tengo deudas viejas por 2000 (super viejas)
221
+ el 1 facturo 1000 que vencen el 20
222
+ el 25 pagó 400.
223
+ Detalle de cálculo de intereses:
224
+ * interés por todo lo viejo (2000) x el rate
225
+ * interés de todo lo que venció en el último período ($600) x días que estuvo vencido (10 días)
226
+ * si además marcó "late payment interest" se agrega interés por los días que pagó tarde, es decir $400 x 5 días
227
+ """
228
+ self .ensure_one ()
229
+
230
+ # Calcular deudas e intereses
231
+ deuda = self ._calculate_debts (from_date , to_date )
232
+
233
+ journal = self .env ['account.journal' ].search ([
234
+ ('type' , '=' , 'sale' ),
235
+ ('company_id' , '=' , self .company_id .id )], limit = 1 )
235
236
236
237
total_items = len (deuda )
237
238
_logger .info ('%s interest invoices will be generated' , total_items )
238
- for idx , partner in enumerate (deuda ):
239
- move_vals = self ._prepare_interest_invoice (partner ,
240
- deuda [partner ], to_date , journal )
241
239
240
+ # Crear facturas
241
+ for idx , partner in enumerate (deuda ):
242
+ move_vals = self ._prepare_interest_invoice (partner , deuda [partner ], to_date , journal )
242
243
if not move_vals :
243
244
continue
244
245
245
- _logger .info ('Creating Interest Invoice (%s of %s) with values: \n %s' , idx + 1 , total_items , partner )
246
+ _logger .info ('Creating Interest Invoice (%s of %s) for partner ID: %s' , idx + 1 , total_items , partner . id )
246
247
247
248
move = self .env ['account.move' ].create (move_vals )
248
-
249
249
if self .automatic_validation :
250
250
try :
251
251
move .action_post ()
@@ -256,7 +256,8 @@ def create_invoices(self, from_date, to_date, groupby=['partner_id']):
256
256
257
257
258
258
259
- def prepare_info (self , to_date ):
259
+
260
+ def _prepare_info (self , to_date ):
260
261
self .ensure_one ()
261
262
262
263
# Format date to customer language
@@ -272,9 +273,12 @@ def prepare_info(self, to_date):
272
273
return res
273
274
274
275
def _prepare_interest_invoice (self , partner , debt , to_date , journal ):
276
+ """
277
+ Retorna un diccionario con los datos para crear la factura
278
+ """
275
279
self .ensure_one ()
276
280
277
- comment = self .prepare_info (to_date )
281
+ comment = self ._prepare_info (to_date )
278
282
fpos = partner .property_account_position_id
279
283
taxes = self .interest_product_id .taxes_id .filtered (
280
284
lambda r : r .company_id == self .company_id )
@@ -299,7 +303,7 @@ def _prepare_interest_invoice(self, partner, debt, to_date, journal):
299
303
"name" : self .interest_product_id .name + '.\n ' + key ,
300
304
"analytic_distribution" : {self .analytic_account_id .id : 100.0 } if self .analytic_account_id .id else False ,
301
305
"tax_ids" : [(6 , 0 , tax_id .ids )]
302
- }) for key , value in debt .items () if value > 0 ],
306
+ }) for key , value in debt .items () if isinstance ( value , ( int , float )) and value > 0 ],
303
307
}
304
308
305
309
# hack para evitar modulo glue con l10n_latam_document
0 commit comments