diff --git a/models/bank_exports_history.py b/models/bank_exports_history.py index 79c3f181ab5d3bccdf8a37b51499b38abd73661c..0ab3fccd83f77cdcfc80879e413b3beb4874ab46 100644 --- a/models/bank_exports_history.py +++ b/models/bank_exports_history.py @@ -13,6 +13,7 @@ class BankExportsHistory(models.Model): impound_balance_line_ids = fields.Many2many('impound.balance.lines', string='Embargos') file_name = fields.Char(string='Nombre', default='rafaela.txt') exported_file = fields.Binary(string='Archivo') + exported_files = fields.Many2many(string="Archivos", comodel_name="ir.attachment") comments = fields.Char(string="Comentarios") export_type = fields.Selection([ ("impound", "Embargos"), diff --git a/views/bank_exports_history_views.xml b/views/bank_exports_history_views.xml index 372f51cf3997a8cbdfb71759b45641d30b09c3fe..f6eba3985f43992ceedefa6cd71437f58665193d 100644 --- a/views/bank_exports_history_views.xml +++ b/views/bank_exports_history_views.xml @@ -17,7 +17,8 @@ <field name="impound_balance_line_ids" readonly="1" attrs="{'invisible': [('impound_balance_line_ids','=', [])]}"/> <field name="file_name" invisible="1"/> <field name="comments" readonly="1"/> - <field widget="binary" height="64" name="exported_file" filename="file_name"/> + <field name="exported_files" widget="many2many_binary" attrs="{'invisible': [('exported_file','!=', False)]}"/> + <field name="exported_file" widget="binary" height="64" filename="file_name" attrs="{'invisible': [('exported_file','=', False)]}"/> <field name="export_type" invisible="1"/> </group> </group> @@ -39,7 +40,7 @@ <field name="loan_ids"/> <field name="impound_balance_line_ids"/> <field name="file_name" invisible="1"/> - <field name="exported_file" filename="file_name"/> + <field name="exported_files" filename="file_name"/> </tree> </field> diff --git a/wizard/export_wizard.py b/wizard/export_wizard.py index 9ae08be442e55f9eb20a36f2e49167f0831984b3..5d8face32d0b08a72003d2379af5e8c01623b3a9 100644 --- a/wizard/export_wizard.py +++ b/wizard/export_wizard.py @@ -152,6 +152,101 @@ class Export_wizard(models.TransientModel): 'impound_balance_line_ids': impound_domain, }} + + + # New methods for create multiple files + def create_file(self, formatter, to_export, bank_constants): + """ + Create a file based on the specified formatter, export data, and bank constants. + + Parameters: + - formatter (Formatter): The formatter object specifying the file format and configuration. + - to_export (list): The list of data to be exported in the file. + - bank_constants (dict): A dictionary containing constants and configuration specific to the bank. + + Returns: + - file (File): The created file object. + - error_log (str): A string describing any error that occurred during file creation. + - id_log (list): A list of identifiers or logs related to the created file. + """ + if formatter.file_name.endswith('.txt'): + return create_txt(formatter, to_export, bank_constants) + else: + return create_xlsx(formatter, to_export) + + def create_attachment(self, file, exported_count, file_name): + """ + Create and return an attachment object for the given file. + + Parameters: + - file (bytes): The binary data of the file to be attached. + - exported_count (int): The count or index of the exported file. + - file_name (str): The base name of the file. + + Returns: + - attachment (Recordset): The created attachment record. + """ + fname = "%s_%s.txt" % (file_name, exported_count) + file_data = { + 'name': fname, + 'datas_fname': fname, + 'datas': base64.b64encode(file), + 'type': 'binary', + } + return self.env['ir.attachment'].create(file_data) + + def generate_files(self, lines, bank_constants, export_type="payroll"): + """ + Generate files for exporting payment data based on specified lines and bank constants. + + Parameters: + - lines (list): A list of dictionaries containing payment information. + - bank_constants (dict): A dictionary containing constants and configuration specific to the bank. + - export_type (str): The type of export (default = "payroll"). + + Returns: + - files (list): A list of created attachment records ids. + - id_logs (list): A list of identifiers or logs related to the created files. + """ + exported_sum, files_count= 0, 1 + to_export, files, id_logs = {}, [], [] + multi_file, bank_limit = self.validate_max_import(self.total_sum) + to_export[files_count] = [] + + for line in lines: + line_total = line["total_payment"] + + if bank_limit > 0: + # Check if a payment exceeds the bank limit + if line_total > bank_limit: + raise ValidationError(_("La nomina de %s [ID %s] supera el importe limite del banco.") % (line["name"], line["id"])) + + # If payment doesnt fit on the cur file, add new file to the list + if (exported_sum + line_total) > bank_limit: + exported_sum = 0 + files_count += 1 + + # Add line to "to_export" list and increment sum. + exported_sum += line["total_payment"] + if not to_export.get(files_count): to_export[files_count] = [] + to_export[files_count].append(line) + + # Loop through groups of lines creating files + for ind, export_group in to_export.items(): + formatter = create_formatter(self.bank_id, export_type) + file, error_log, id_log = self.create_file(formatter, export_group, bank_constants) + id_logs += id_log + + if error_log: + raise ValidationError(_(error_log)) + + if file: + new_attachment = self.create_attachment(file, ind, formatter.file_name.split(".")[0]) + files.append(new_attachment) + + file_ids = [file.id for file in files] + return file_ids, id_logs + def export_loan(self, loan_ids): if not loan_ids: loan_ids = self.env['hr.employee.loans'].search([ @@ -171,14 +266,14 @@ class Export_wizard(models.TransientModel): } # Check for max import limit - loans_sum = 0 + self.total_sum = 0 loan_records = [] for loan in loan_ids: emp = loan.employee_id bank_data = emp.get_bank_data(self.bank_id.id, bank_code=self.bank_id.select_bank) total_payment = (float(loan.total_import) * float(bank_data['porcent'])) / 100 - loans_sum += total_payment + self.total_sum += total_payment record = { 'id': loan.id, 'name': loan.employee_id.name, @@ -207,39 +302,24 @@ class Export_wizard(models.TransientModel): for cte in self.bank_id.constants_ids.filtered(lambda r: r.export_type != 'impound'): bank_constants[cte.constant_order - 1] = cte.value - self.validate_max_import(loans_sum) - # exporting txt - formatter = create_formatter(self.bank_id, 'loan') - if formatter.file_name.endswith('.txt'): - f, error_log, id_log = create_txt(formatter, loan_records, bank_constants) - else: - f, error_log, id_log = create_xlsx(formatter, loan_records) - if error_log: - raise ValidationError(_(error_log)) - - if not error_log: - error_log = "Todas las nominas fueron añadidas satisfactoriamente." + # Generate files from function + file_ids, id_logs = self.generate_files(loan_records, bank_constants, "loan") # Filtering loans with missing fields. # Filtered loans ids - fp = [loan.id for loan in loan_ids if loan.id not in id_log] - loans = self.env['hr.employee.loans'].browse(fp) + loans = loan_ids.filtered(lambda x: x.id not in id_logs) # Saving into history - bank_export = self.env['bank.exports'].create( - { - 'file_name': formatter.file_name, - 'bank_id': self.bank_id.id, - 'loan_ids': [(6,0, loans.ids)], - 'exported_file': base64.b64encode(f), - 'export_date': datetime.now(), - 'comments': self.comments, - 'export_type': 'loan' - } - ) + bank_export = self.env['bank.exports'].create({ + 'file_name': "", + 'bank_id': self.bank_id.id, + "exported_files": [(6,0, file_ids)], + 'loan_ids': [(6,0, loans.ids)], + 'export_date': datetime.now(), + 'comments': self.comments, + 'export_type': 'loan' + }) - # Creating info message - # value = self.env['hr.status.exportation.message'].create({'text': error_log}) return { 'name': 'Exportacion bancaria', 'type': 'ir.actions.act_window', @@ -268,7 +348,7 @@ class Export_wizard(models.TransientModel): } # Check for max import limit - payslips_sum = 0 + self.total_sum = 0 payslip_records = [] @@ -286,7 +366,7 @@ class Export_wizard(models.TransientModel): bank_list.append(dic_bank) total_payment = (float(payslip.total_payment) * float(bank_list[0]['porcent'])) / 100 - payslips_sum += total_payment + self.total_sum += total_payment record = { 'id': payslip.id, @@ -325,35 +405,24 @@ class Export_wizard(models.TransientModel): if payslip.total_paid >= payslip.total_payment: payslip.write({'exported': True}) - self.validate_max_import(payslips_sum) - - # Exporting txt - formatter = create_formatter(self.bank_id, 'payroll') - if formatter.file_name.endswith('.txt'): - f, error_log, id_log = create_txt(formatter, payslip_records, bank_constants) - else: - f, error_log, id_log = create_xlsx(formatter, payslip_records) - if error_log: - raise ValidationError(_(error_log)) + # Generate files from function + file_ids, id_logs = self.generate_files(payslip_records, bank_constants) # Filtering payslips with missing fields. # Filtered payslips ids - payslips = paylisp_ids.filtered(lambda x: x.id not in id_log) - - #Saving into history + payslips = paylisp_ids.filtered(lambda x: x.id not in id_logs) + # Saving into history bank_export = self.env['bank.exports'].create({ - 'file_name': formatter.file_name, + 'file_name': "", 'bank_id': self.bank_id.id, + "exported_files": [(6,0, file_ids)], 'payslip_ids': [(6,0, payslips.ids)], - 'exported_file': base64.b64encode(f), 'export_date': datetime.now(), 'comments': self.comments, 'export_type': 'payroll' }) - #Creating info message - # value = self.env['hr.status.exportation.message'].create({'text': error_log}) return { 'name': 'Exportacion bancaria', 'type': 'ir.actions.act_window', @@ -386,12 +455,12 @@ class Export_wizard(models.TransientModel): 'target': 'new', } - # Check for max import limit - self.validate_max_import(self.total_impounds_amount) - impound_records = [] impound_records_dict = {} + # Check for max import limit + self.total_sum = 0 + for impound_line in impound_balance_line_ids: bank_list = [] employee = impound_line.impounds_id.employee @@ -434,6 +503,7 @@ class Export_wizard(models.TransientModel): impound_records_dict[record_key] = record impound_records = list(impound_records_dict.values()) + self.total_sum += record["total_payment"] # # This step is made for making sure order of every constant bank_constants = [None for _ in range(25)] @@ -455,25 +525,19 @@ class Export_wizard(models.TransientModel): if not impound_line.exported: impound_line.write({'exported': True}) - # Exporting txt - formatter = create_formatter(self.bank_id, 'impound') - if formatter.file_name.endswith('.txt'): - f, error_log, id_log = create_txt(formatter, impound_records, bank_constants) - else: - f, error_log, id_log = create_xlsx(formatter, impound_records) - if error_log: - raise ValidationError(_(error_log)) + # Generate files from function + file_ids, id_logs = self.generate_files(impound_records, bank_constants, "impound") # Filtering payslips with missing fields. # Filtered impound_lines ids - impounds = impound_balance_line_ids.filtered(lambda x: x.id not in id_log) + impounds = impound_balance_line_ids.filtered(lambda x: x.id not in id_logs) - #Saving into history + # Saving into history bank_export = self.env['bank.exports'].create({ - 'file_name': formatter.file_name, + 'file_name': "", 'bank_id': self.bank_id.id, - 'impound_balance_line_ids': [(6, 0, impounds.ids)], - 'exported_file': base64.b64encode(f), + "exported_files": [(6,0, file_ids)], + 'impound_balance_line_ids': [(6,0, impounds.ids)], 'export_date': datetime.now(), 'comments': self.comments, 'export_type': 'impound' @@ -497,7 +561,20 @@ class Export_wizard(models.TransientModel): return self.export_impound(self.impound_balance_line_ids) def validate_max_import(self, value_sum): - # Get max import value from bank's constants - max_import = next((i.value for i in self.bank_id.constants_ids if i.constant_order == 25), False) - if ( max_import and float_compare(value_sum, float(max_import), precision_digits=2) > 0 ): - raise UserError(_("Se ha superado el limite de importe para el banco seleccionado (Máximo {:.2f})".format(float(max_import)))) + """ + Validates if the given `value_sum` exceeds the maximum import value defined in the bank's constants. + + Parameters: + - value_sum (float): The sum of values to be validated against the maximum import value. + + Returns: + tuple: + 1. A boolean indicating whether the `value_sum` exceeds the maximum import value. + 2. The maximum import value obtained from the bank's constants. + + Note: + The maximum import value is obtained by searching for the constant order 25, if not found then bank didnt have + maximum import limit. + """ + max_import = next((i.value for i in self.bank_id.constants_ids if i.constant_order == 25), 0) + return ( max_import and float_compare(value_sum, float(max_import), precision_digits=2) > 0 ), int(max_import)