from odoo import models, fields, api, _ from odoo.exceptions import ValidationError from datetime import datetime import base64 import xlrd class UploadPayments(models.Model): _name = "upload.payments" _description = "Upload Payments" _order = "create_date desc" payments_lines = fields.One2many('upload.payments.line', 'upload_payments_id', string='Upload Payments Lines', auto_join=True, copy=False) number = fields.Char('Number', copy=False) date_upload = fields.Date('Upload Date', copy=False, default=fields.Date.context_today) user_id = fields.Many2one('res.users', string='Created By', copy=False, default=lambda self: self.env.user.id) excel_file = fields.Binary('Excel File', attachment=True) filename = fields.Char('File Name') @api.model def create(self, vals): vals['number'] = self.env['ir.sequence'].next_by_code('upload.payments') or '/' return super(UploadPayments, self).create(vals) def action_import_excel(self): self.ensure_one() if not self.excel_file: raise ValidationError(_("Please upload an Excel file first.")) try: file_content = base64.b64decode(self.excel_file) workbook = xlrd.open_workbook(file_contents=file_content) sheet = workbook.sheet_by_index(0) except: raise ValidationError(_("Invalid Excel file format.")) # Process Excel rows (skip header row if exists) line_vals_list = [] for row in range(1, sheet.nrows): try: no_invoice = str(sheet.cell(row, 0).value).strip() date_invoice = xlrd.xldate.xldate_as_datetime(sheet.cell(row, 1).value, workbook.datemode).date() line_vals = { 'no_invoice': no_invoice, 'date_invoice': date_invoice, } line_vals_list.append((0, 0, line_vals)) except: continue # Update current record with lines self.write({ 'payments_lines': line_vals_list, }) return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': _('Success'), 'message': _('Imported %s lines from Excel.') % len(line_vals_list), 'sticky': False, 'next': {'type': 'ir.actions.act_window_close'}, } } def action_create_payments(self): """Membuat payment untuk semua lines yang belum memiliki payment""" self.ensure_one() created_payments = [] for line in self.payments_lines.filtered(lambda l: not l.payment_id): invoice = self.env['account.move'].search([ ('invoice_marketplace', '=', line.no_invoice), ('state', '=', 'posted'), ('payment_state', '!=', 'paid'), ]) for move in invoice: move._register_payment_automatically(line.date_invoice) def queue_job(self): self.date_upload = fields.Datetime.now() lines = self.payments_lines.filtered(lambda l: not l.payment_id) existing_jobs = self.env['queue.job'].search([ ('model_name', '=', 'upload.payments.line'), ('method_name', '=', 'action_create_payments'), ('res_id', 'in', lines.ids), ('state', '!=', 'error'), ]) existing_res_ids = set(existing_jobs.mapped('res_id')) jobs_to_create = [] for line in lines: if line.id in existing_res_ids: continue jobs_to_create.append({ 'name': f'Upload Payments {line.no_invoice}', 'model_name': 'upload.payments.line', 'method_name': 'action_create_payments', 'res_id': line.id, }) self.env['queue.job'].create(jobs_to_create) class UploadPaymentsLine(models.Model): _name = "upload.payments.line" _description = "Upload Payments Line" _inherit = ['mail.thread'] upload_payments_id = fields.Many2one('upload.payments', string='Upload Payments') no_invoice = fields.Char('Invoice Number', required=True) date_invoice = fields.Date('Invoice Date', required=True) move_id = fields.Many2one('account.move', string='Invoice') payment_id = fields.Many2one('account.payment', string='Created Payment') def action_create_payments(self): self.ensure_one() invoice = self.env['account.move'].search([ ('invoice_marketplace', '=', self.no_invoice), ('state', '=', 'posted'), ('payment_state', '!=', 'paid'), ]) for move in invoice: move._register_payment_automatically(self.date_invoice)