diff options
| author | Azka Nathan <darizkyfaz@gmail.com> | 2025-08-14 16:28:15 +0700 |
|---|---|---|
| committer | Azka Nathan <darizkyfaz@gmail.com> | 2025-08-14 16:28:15 +0700 |
| commit | bfdaf12dc3e7abace11502fcf1cb6c87522f1a51 (patch) | |
| tree | 1b7d44f2b6a5ad2539f181effb66e9e3a59c9e9a | |
| parent | ad6ccce331dc8c83f2cacb82b0e3c94467ff9d85 (diff) | |
| parent | db75081a2afcd369e8a0169f3fe2f080dbad0c4a (diff) | |
Merge branch 'odoo-backup' of bitbucket.org:altafixco/indoteknik-addons into odoo-backup
| -rw-r--r-- | indoteknik_api/controllers/api_v1/partner.py | 4 | ||||
| -rw-r--r-- | indoteknik_api/controllers/api_v1/sale_order.py | 21 | ||||
| -rw-r--r-- | indoteknik_api/models/sale_order.py | 10 | ||||
| -rw-r--r-- | indoteknik_custom/models/account_move.py | 73 | ||||
| -rw-r--r-- | indoteknik_custom/models/res_partner.py | 4 | ||||
| -rwxr-xr-x | indoteknik_custom/models/sale_order.py | 30 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_picking.py | 8 | ||||
| -rw-r--r-- | indoteknik_custom/models/tukar_guling.py | 60 | ||||
| -rw-r--r-- | indoteknik_custom/models/tukar_guling_po.py | 60 | ||||
| -rw-r--r-- | indoteknik_custom/report/report_sale_order.xml | 6 | ||||
| -rw-r--r-- | indoteknik_custom/views/tukar_guling.xml | 9 | ||||
| -rw-r--r-- | indoteknik_custom/views/tukar_guling_po.xml | 10 |
12 files changed, 205 insertions, 90 deletions
diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index b8bd21be..acec19f7 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -150,7 +150,7 @@ class Partner(controller.Controller): partner_params = self.get_request_params(request_data, { 'tax_name': ['alias:nama_wajib_pajak'], - 'company_type_id': ['number'], + 'company_type_id': [''], 'industry_id': ['number'], 'npwp': [], 'alamat_lengkap_text': [], @@ -170,7 +170,7 @@ class Partner(controller.Controller): if 'id_user' in request_data: user_params = self.get_request_params(request_data, { 'id_user': ['required', 'number'], - 'company_type_id': ['number'], + 'company_type_id': [''], 'industry_id': ['number'], 'tax_name': ['alias:nama_wajib_pajak'], 'npwp': [], diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index 6f5a3d44..d199cd84 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -87,6 +87,8 @@ class SaleOrder(controller.Controller): 'amount_tax': sale.amount_tax, 'amount_total': sale.amount_total, 'expected_ready_to_ship': f"{sale.expected_ready_to_ship.day} {INDONESIAN_MONTHS[sale.expected_ready_to_ship.month]} {sale.expected_ready_to_ship.year}", + 'eta_date_start': f"{sale.eta_date_start.day} {INDONESIAN_MONTHS[sale.eta_date_start.month]} {sale.eta_date_start.year}", + 'eta_date_end': f"{sale.eta_date.day} {INDONESIAN_MONTHS[sale.eta_date.month]} {sale.eta_date.year}", 'product_name': product_name, 'product_not_in_id': product_not_in_id, 'details': [request.env['sale.order.line'].api_single_response(x, context='with_detail') for x in sale.order_line] @@ -257,6 +259,25 @@ class SaleOrder(controller.Controller): bulan = bulan_id[sale_order.expected_ready_to_ship.month - 1] tahun = sale_order.expected_ready_to_ship.year data['expected_ready_to_ship'] = f"{tanggal} {bulan} {tahun}" + if sale_order.eta_date_start: + bulan_id = [ + "Januari", "Februari", "Maret", "April", "Mei", "Juni", + "Juli", "Agustus", "September", "Oktober", "November", "Desember" + ] + tanggal = sale_order.eta_date_start.day + bulan = bulan_id[sale_order.eta_date_start.month - 1] + tahun = sale_order.eta_date_start.year + data['eta_date_start'] = f"{tanggal} {bulan} {tahun}" + + if sale_order.eta_date: + bulan_id = [ + "Januari", "Februari", "Maret", "April", "Mei", "Juni", + "Juli", "Agustus", "September", "Oktober", "November", "Desember" + ] + tanggal = sale_order.eta_date.day + bulan = bulan_id[sale_order.eta_date.month - 1] + tahun = sale_order.eta_date.year + data['eta_date_end'] = f"{tanggal} {bulan} {tahun}" return self.response(data) diff --git a/indoteknik_api/models/sale_order.py b/indoteknik_api/models/sale_order.py index 0561043b..9be03927 100644 --- a/indoteknik_api/models/sale_order.py +++ b/indoteknik_api/models/sale_order.py @@ -29,6 +29,9 @@ class SaleOrder(models.Model): 'approval_step': APPROVAL_STEP[sale_order.web_approval] if sale_order.web_approval else 0, 'date_order': self.env['rest.api'].datetime_to_str(sale_order.date_order, '%d/%m/%Y %H:%M:%S'), 'payment_type': sale_order.payment_type, + 'carrier_id': sale_order.carrier_id.id, + 'carrier_name': sale_order.carrier_id.name, + 'service_type': sale_order.shipping_option_id.name, 'pickings': [] } for picking in sale_order.picking_ids: @@ -47,6 +50,13 @@ class SaleOrder(models.Model): 'eta' : response['eta'], 'id': picking.id, 'name': picking.name, + 'products': [{ + 'id': product.id, + 'name': product.name, + 'image': self.env['ir.attachment'].api_image('product.template', 'image_128', product.product_tmpl_id.id), + 'code': product.default_code or '' + } for product in picking.move_line_ids.product_id], + 'product_count': len(picking.move_line_ids) # 'tracking_number': picking.delivery_tracking_no or '', # 'delivered': picking.waybill_id.delivered or picking.driver_arrival_date != False or picking.sj_return_date != False, }) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index fd08ed60..b0ffd8b9 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -97,6 +97,8 @@ class AccountMove(models.Model): payment_date = fields.Date(string="Payment Date", compute='_compute_payment_date') partial_payment = fields.Float(string="Partial Payment", compute='compute_partial_payment') + reminder_sent_date = fields.Date(string="Tanggal Reminder Terkirim") + def compute_partial_payment(self): for move in self: if move.amount_total_signed > 0 and move.amount_residual_signed > 0 and move.payment_state == 'partial': @@ -119,20 +121,6 @@ class AccountMove(models.Model): else: move.payment_date = False - # def name_get(self): - # result = [] - # for move in self: - # if move.move_type == 'entry': - # # Jika masih draft, tampilkan 'Draft CAB' - # if move.state == 'draft': - # label = 'Draft CAB' - # else: - # label = move.name - # result.append((move.id, label)) - # else: - # # Untuk invoice dan lainnya, pakai default - # result.append((move.id, move.display_name)) - # return result def send_due_invoice_reminder(self): today = fields.Date.today() @@ -144,31 +132,22 @@ class AccountMove(models.Model): today + timedelta(days=7), ] - # --- TESTING --- - # partner = self.env['res.partner'].search([('name', 'ilike', 'DIRGANTARA YUDHA ARTHA')], limit=1) - # if not partner: - # _logger.info("Partner tidak ditemukan.") - # return - # invoices = self.env['account.move'].search([ - # ('move_type', '=', 'out_invoice'), - # ('state', '=', 'posted'), - # ('payment_state', 'not in', ['paid', 'in_payment', 'reversed']), - # ('invoice_date_due', 'in', target_dates), - # ('partner_id', '=', partner.id), - # ]) + for days_after_due in range(14, 181, 7): + target_dates.append(today - timedelta(days=days_after_due)) invoices = self.env['account.move'].search([ ('move_type', '=', 'out_invoice'), ('state', '=', 'posted'), ('payment_state', 'not in', ['paid', 'in_payment', 'reversed']), ('invoice_date_due', 'in', target_dates), + ('date_terima_tukar_faktur', '!=', False) ]) - _logger.info(f"Invoices tahap 1: {invoices}") + _logger.info(f"Invoices: {invoices}") invoices = invoices.filtered( lambda inv: inv.invoice_payment_term_id and 'tempo' in (inv.invoice_payment_term_id.name or '').lower() ) - _logger.info(f"Invoices tahap 2: {invoices}") + # _logger.info(f"Invoices tahap 2: {invoices}") if not invoices: _logger.info("Tidak ada invoice yang due") @@ -177,11 +156,18 @@ class AccountMove(models.Model): invoice_group = {} for inv in invoices: dtd = (inv.invoice_date_due - today).days if inv.invoice_date_due else 0 - invoice_group.setdefault((inv.partner_id, dtd), []).append(inv) + key = (inv.partner_id, dtd) + if key not in invoice_group: + invoice_group[key] = self.env['account.move'] # recordset kosong + invoice_group[key] |= inv # gabung recordset template = self.env.ref('indoteknik_custom.mail_template_invoice_due_reminder') for (partner, dtd), invs in invoice_group.items(): + if all(inv.reminder_sent_date == today for inv in invs): + _logger.info(f"Reminder untuk {partner.name} sudah terkirim hari ini, skip.") + continue + # Ambil child contact yang di-checklist reminder_invoices reminder_contacts = self.env['res.partner'].search([ ('parent_id', '=', partner.id), @@ -190,11 +176,16 @@ class AccountMove(models.Model): ]) _logger.info(f"Email Reminder Child {reminder_contacts}") - if not reminder_contacts: - _logger.info(f"Partner {partner.name} tidak memiliki email yang sudah ceklis reminder") - continue + # if not reminder_contacts: + # _logger.info(f"Partner {partner.name} tidak memiliki email yang sudah ceklis reminder") + # continue emails = list(filter(None, [partner.email])) + reminder_contacts.mapped('email') + if reminder_contacts: + _logger.info(f"Email Reminder Child {reminder_contacts}") + else: + _logger.info(f"Tidak ada child contact reminder, gunakan email utama partner") + if not emails: _logger.info(f"Partner {partner.name} tidak memiliki email yang bisa dikirimi") continue @@ -208,7 +199,7 @@ class AccountMove(models.Model): invoice_table_rows += f""" <tr> <td>{inv.partner_id.name}</td> - <td>{inv.purchase_order_id.name or '-'}</td> + <td>{inv.ref or '-'}</td> <td>{inv.name}</td> <td>{fields.Date.to_string(inv.invoice_date) or '-'}</td> <td>{fields.Date.to_string(inv.invoice_date_due) or '-'}</td> @@ -220,9 +211,9 @@ class AccountMove(models.Model): days_to_due_message = "" closing_message = "" - if dtd < 0: + if dtd > 0: days_to_due_message = ( - f"Kami ingin mengingatkan bahwa tagihan anda akan jatuh tempo dalam {abs(dtd)} hari ke depan, " + f"Kami ingin mengingatkan bahwa tagihan anda akan jatuh tempo dalam {dtd} hari ke depan, " "dengan rincian sebagai berikut:" ) closing_message = ( @@ -244,9 +235,9 @@ class AccountMove(models.Model): "Terima kasih atas perhatian dan kerja samanya." ) - if dtd > 0: + if dtd < 0: days_to_due_message = ( - f"Kami ingin mengingatkan bahwa tagihan anda telah jatuh tempo selama {dtd} hari, " + f"Kami ingin mengingatkan bahwa tagihan anda telah jatuh tempo selama {abs(dtd)} hari, " "dengan rincian sebagai berikut:" ) closing_message = ( @@ -282,18 +273,18 @@ class AccountMove(models.Model): # 'email_to': 'andrifebriyadiputra@gmail.com', 'email_to': email_to, 'email_from': 'finance@indoteknik.co.id', - 'email_cc': ",".join(cc_list), + 'email_cc': ",".join(sorted(set(cc_list))), 'body_html': body_html, 'reply_to': 'finance@indoteknik.co.id', } - _logger.info(f"Mengirim email ke: {values['email_to']} CC: {values['email_cc']}") + _logger.info(f"Mengirim email ke: {values['email_to']} > email CC: {values['email_cc']}") template.send_mail(invs[0].id, force_send=True, email_values=values) - + # flag + invs.write({'reminder_sent_date': today}) # Post ke chatter user_system = self.env['res.users'].browse(25) system_id = user_system.partner_id.id if user_system else False - for inv in invs: inv.message_post( subject=values['subject'], diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index f260f58e..cf9fbea4 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -231,7 +231,7 @@ class ResPartner(models.Model): rec.payment_difficulty = rec.parent_id.payment_difficulty return records - @api.constrains('name') + @api.constrains('email') def _check_duplicate_name(self): for record in self: if record.name: @@ -242,7 +242,7 @@ class ResPartner(models.Model): ], limit=1) if existing_partner: - raise ValidationError(f"Nama '{record.name}' sudah digunakan oleh partner lain!") + raise ValidationError(f"Nama '{record.name}' dengan email '{record.email}' sudah digunakan oleh partner lain!") @api.constrains('npwp') def _check_npwp(self): diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index e71e3830..0acfa0b0 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1116,8 +1116,9 @@ class SaleOrder(models.Model): break if not selected_option and shipping_options: + if not self.env.context.get('from_website_checkout'): + _logger.info(f"[DEFAULT] Tidak ada yang cocok, pakai opsi pertama: {shipping_options[0].name}") selected_option = shipping_options[0] - _logger.info(f"[DEFAULT] Tidak ada yang cocok, pakai opsi pertama: {selected_option.name}") # ❗ Ganti carrier_id hanya jika BELUM terisi sama sekali (contoh: user dari backend) if not self.carrier_id: @@ -1137,9 +1138,18 @@ class SaleOrder(models.Model): # Set shipping option dan nilai ongkir if selected_option: - self.shipping_option_id = selected_option.id - self.delivery_amt = selected_option.price - self.delivery_service_type = selected_option.courier_service_code + if self.env.context.get('from_website_checkout'): + # Simpan di context sebagai nilai sementara + self = self.with_context( + _temp_delivery_amt=selected_option.price, + _temp_delivery_service=selected_option.courier_service_code, + _temp_shipping_option=selected_option.id + ) + else: + self.shipping_option_id = selected_option.id + self.delivery_amt = selected_option.price + self.delivery_service_type = selected_option.courier_service_code + message_lines = [f"<b>Estimasi Ongkos Kirim Biteship:</b><br/>"] for courier, options in courier_options.items(): @@ -2178,7 +2188,7 @@ class SaleOrder(models.Model): self.check_product_bom() self.check_credit_limit() self.check_limit_so_to_invoice() - order.approval_status = 'approved' + order.approval_status = 'pengajuan1' return self._create_approval_notification('Team Sales') raise UserError("Bisa langsung Confirm") @@ -2408,7 +2418,7 @@ class SaleOrder(models.Model): order.approval_status = 'pengajuan1' return self._create_approval_notification('Sales Manager') elif order._requires_approval_team_sales(): - order.approval_status = 'approved' + order.approval_status = 'pengajuan1' return self._create_approval_notification('Team Sales') order.approval_status = 'approved' @@ -2950,6 +2960,14 @@ class SaleOrder(models.Model): order.select_shipping_option = 'biteship' order.action_estimate_shipping() + temp_price = self.env.context.get('_temp_delivery_amt') + temp_service = self.env.context.get('_temp_delivery_service') + temp_option_id = self.env.context.get('_temp_shipping_option') + if temp_price and temp_option_id: + order.shipping_option_id = temp_option_id + order.delivery_amt = temp_price + order.delivery_service_type = temp_service + # Restore pilihan user setelah estimasi if user_carrier_id and user_service: # Dapatkan provider diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 0354587c..bf6834d0 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -19,10 +19,10 @@ import re _logger = logging.getLogger(__name__) _biteship_url = "https://api.biteship.com/v1" -biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" +# biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" -# biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" +biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" class StockPicking(models.Model): @@ -1714,7 +1714,9 @@ class StockPicking(models.Model): if move_line.qty_done > 0: product_shipped.append({ 'name': move_line.product_id.name, - 'qty': move_line.qty_done + 'code': move_line.product_id.default_code, + 'qty': move_line.qty_done, + 'image': self.env['ir.attachment'].api_image('product.template', 'image_128', move_line.product_id.product_tmpl_id.id), }) response = { diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 4c7722d5..4b03d4b0 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -32,7 +32,7 @@ class TukarGuling(models.Model): 'tukar_guling_id', string='Transfers' ) - # origin_so = fields.Many2one('sale.order', string='Origin SO') + origin_so = fields.Many2one('sale.order', string='Origin SO', compute='_compute_origin_so') name = fields.Char('Number', required=True, copy=False, readonly=True, default='New') date = fields.Datetime('Date', default=fields.Datetime.now, required=True) operations = fields.Many2one( @@ -83,19 +83,47 @@ class TukarGuling(models.Model): invoice_id = fields.Many2many('account.move', string='Invoice Ref', readonly=True) - @api.depends('origin') + @api.depends('origin', 'operations') + def _compute_origin_so(self): + for rec in self: + rec.origin_so = False + origin_str = rec.origin or rec.operations.origin + if origin_str: + so = self.env['sale.order'].search([('name', '=', origin_str)], limit=1) + rec.origin_so = so.id if so else False + + @api.depends('origin', 'origin_so', 'partner_id', 'line_ids.product_id') def _compute_is_has_invoice(self): + Move = self.env['account.move'] for rec in self: - invoices = self.env['account.move'].search([ - ('invoice_origin', 'ilike', rec.origin), - ('move_type', '=', 'out_invoice'), # hanya invoice - ('state', 'not in', ['draft', 'cancel']) - ]) + rec.is_has_invoice = False + rec.invoice_id = [(5, 0, 0)] + + product_ids = rec.line_ids.mapped('product_id').ids + if not product_ids: + continue + + domain = [ + ('move_type', 'in', ['out_invoice', 'in_invoice']), + ('state', 'not in', ['draft', 'cancel']), + ('invoice_line_ids.product_id', 'in', product_ids), + ] + + if rec.partner_id: + domain.append(('partner_id', '=', rec.partner_id.id)) + + extra = [] + if rec.origin: + extra.append(('invoice_origin', 'ilike', rec.origin)) + if rec.origin_so: + extra.append(('invoice_line_ids.sale_line_ids.order_id', '=', rec.origin_so.id)) + if extra: + domain = domain + ['|'] * (len(extra) - 1) + extra + + invoices = Move.search(domain).with_context(active_test=False) if invoices: + rec.invoice_id = [(6, 0, invoices.ids)] rec.is_has_invoice = True - rec.invoice_id = invoices - else: - rec.is_has_invoice = False def set_opt(self): if not self.val_inv_opt and self.is_has_invoice == True: @@ -144,8 +172,6 @@ class TukarGuling(models.Model): if self.line_ids and from_return_picking: # Hanya update origin, jangan ubah lines - if self.operations.origin: - self.origin = self.operations.origin _logger.info("📌 Menggunakan product lines dari return wizard, tidak populate ulang.") # 🚀 Tapi tetap populate mapping koli jika BU/OUT @@ -177,6 +203,7 @@ class TukarGuling(models.Model): # Set origin dari operations if self.operations.origin: self.origin = self.operations.origin + self.origin_so = self.operations.group_id.id # Auto-populate lines dari move_ids operations lines_data = [] @@ -332,17 +359,20 @@ class TukarGuling(models.Model): # _("Tidak bisa memilih Return Type 'Revisi SO' karena dokumen %s sudah dibuat invoice.") % record.origin # ) + @api.model def create(self, vals): - # Generate sequence number if not vals.get('name') or vals['name'] == 'New': vals['name'] = self.env['ir.sequence'].next_by_code('tukar.guling') - # Auto-fill origin from operations - if not vals.get('origin') and vals.get('operations'): + if vals.get('operations'): picking = self.env['stock.picking'].browse(vals['operations']) if picking.origin: vals['origin'] = picking.origin + # Find matching SO + so = self.env['sale.order'].search([('name', '=', picking.origin)], limit=1) + if so: + vals['origin_so'] = so.id if picking.partner_id: vals['partner_id'] = picking.partner_id.id diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index 0badc117..cc1c79c0 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -62,20 +62,57 @@ class TukarGulingPO(models.Model): is_has_bill = fields.Boolean('Has Bill?', compute='_compute_is_has_bill', readonly=True, default=False) bill_id = fields.Many2many('account.move', string='Bill Ref', readonly=True) + origin_po = fields.Many2one('purchase.order', string='Origin PO', compute='_compute_origin_po') - @api.depends('origin') + @api.depends('origin', 'operations') + def _compute_origin_po(self): + for rec in self: + rec.origin_po = False + origin_str = rec.origin or rec.operations.origin + if origin_str: + so = self.env['purchase.order'].search([('name', '=', origin_str)], limit=1) + rec.origin_po = so.id if so else False + + @api.depends('origin', 'origin_po', 'vendor_id', 'line_ids.product_id') def _compute_is_has_bill(self): + Move = self.env['account.move'] for rec in self: - bills = self.env['account.move'].search([ - ('invoice_origin', 'ilike', rec.origin), - ('move_type', '=', 'in_invoice'), # hanya vendor bill - ('state', 'not in', ['draft', 'cancel']) - ]) - if bills: - rec.is_has_bill = True - rec.bill_id = bills - else: - rec.is_has_bill = False + # reset + rec.is_has_bill = False + rec.bill_id = [(5, 0, 0)] + + product_ids = rec.line_ids.mapped('product_id').ids + if not product_ids: + continue + + # dasar: bill atau vendor credit note yang linennya mengandung produk TG + domain = [ + ('move_type', 'in', ['in_invoice', 'in_refund']), + ('state', 'not in', ['draft', 'cancel']), + ('invoice_line_ids.product_id', 'in', product_ids), + ] + + # batasi ke vendor sama (kalau ada) + if rec.vendor_id: + domain.append(('partner_id', '=', rec.vendor_id.id)) + + # bantu pembatasan ke asal dokumen + extra = [] + if rec.origin: + extra.append(('invoice_origin', 'ilike', rec.origin)) + if rec.origin_po: + # di Odoo 14, invoice line biasanya link ke purchase.line lewat purchase_line_id + extra.append(('invoice_line_ids.purchase_line_id.order_id', '=', rec.origin_po.id)) + + # OR-kan semua extra filter jika ada + if extra: + domain = domain + ['|'] * (len(extra) - 1) + extra + + bills = Move.search(domain).with_context(active_test=False) + + # --- Opsi 1: minimal salah satu produk TG muncul di bill (default) --- + rec.bill_id = [(6, 0, bills.ids)] + rec.is_has_bill = bool(bills) def set_opt(self): if not self.val_bil_opt and self.is_has_bill == True: @@ -133,6 +170,7 @@ class TukarGulingPO(models.Model): # Hanya update origin, jangan ubah lines if self.operations.origin: self.origin = self.operations.origin + self.origin_po = self.operations.group_id.id return if from_return_picking: diff --git a/indoteknik_custom/report/report_sale_order.xml b/indoteknik_custom/report/report_sale_order.xml index b9928790..d1a6de28 100644 --- a/indoteknik_custom/report/report_sale_order.xml +++ b/indoteknik_custom/report/report_sale_order.xml @@ -96,8 +96,8 @@ <th name="th_description" class="text-left">Description</th> <th name="th_quantity" class="text-right">Quantity</th> <th name="th_priceunit" class="text-right">Unit Price</th> - <th name="th_discount" t-if="display_discount" class="text-right" groups="product.group_discount_per_so_line"> - <span>Disc.%</span> + <th name="th_discount" class="text-right" groups="product.group_discount_per_so_line"> + Disc.% </th> <th name="th_taxes" class="text-right">Taxes</th> <th name="th_subtotal" class="text-right"> @@ -125,7 +125,7 @@ <td name="td_priceunit" class="text-right"> <span t-field="line.price_unit"/> </td> - <td t-if="display_discount" class="text-right" groups="product.group_discount_per_so_line"> + <td class="text-right" groups="product.group_discount_per_so_line"> <span t-field="line.discount"/> </td> <td name="td_taxes" class="text-right"> diff --git a/indoteknik_custom/views/tukar_guling.xml b/indoteknik_custom/views/tukar_guling.xml index 20aa2eef..9dd31905 100644 --- a/indoteknik_custom/views/tukar_guling.xml +++ b/indoteknik_custom/views/tukar_guling.xml @@ -21,7 +21,7 @@ <field name="name">pengajuan.tukar.guling.tree</field> <field name="model">tukar.guling</field> <field name="arch" type="xml"> - <tree create="1" delete="1" default_order="create_date desc"> + <tree create="0" delete="1" default_order="create_date desc"> <field name="name"/> <field name="partner_id" string="Customer"/> <field name="origin" string="SO Number"/> @@ -83,9 +83,9 @@ <field name="return_type" attrs="{'readonly': [('state', 'not in', 'draft')]}"/> <field name="operations" attrs="{'readonly': [('state', 'not in', 'draft')]}"/> - <field name="origin" readonly="1"/> +<!-- <field name="origin" readonly="1"/>--> + <field name="origin_so" readonly="1"/> <field name="is_has_invoice" readonly="1"/> - <field name="invoice_id" readonly="1" widget="many2many_tags"/> </group> <group> <field name="val_inv_opt" attrs="{'invisible': [('is_has_invoice', '=', False)]}"/> @@ -95,6 +95,9 @@ <field name="date_finance" readonly="1"/> <field name="date_logistic" readonly="1"/> </group> + <group> + <field name="invoice_id" readonly="1" attrs="{'invisible': [('is_has_invoice', '=', False)]}"/> + </group> </group> <notebook> <page string="Product Lines" name="product_lines"> diff --git a/indoteknik_custom/views/tukar_guling_po.xml b/indoteknik_custom/views/tukar_guling_po.xml index bc696a3d..1c6a86ea 100644 --- a/indoteknik_custom/views/tukar_guling_po.xml +++ b/indoteknik_custom/views/tukar_guling_po.xml @@ -24,7 +24,7 @@ <tree create="1" delete="1" default_order="create_date desc"> <field name="name"/> <field name="vendor_id" string="Customer"/> - <field name="origin" string="SO Number"/> + <field name="origin" string="PO Number"/> <field name="operations" string="Operations"/> <field name="return_type" string="Return Type"/> <field name="state" widget="badge" @@ -89,11 +89,10 @@ attrs="{ 'required': [('return_type', 'in', ['revisi_po', 'tukar_guling'])] }"/> - <field name="origin" readonly="1"/> +<!-- <field name="origin" readonly="1"/>--> + <field name="origin_po" readonly="1"/> <field name="is_has_bill" readonly="1"/> <!-- <field name="bill_id" readonly="1" />--> - <field name="bill_id" readonly="1" widget="many2many_tags"/> - <!-- <field name="origin_so" readonly="1"/>--> </group> <group> <field name="val_bil_opt" attrs="{'invisible': [('is_has_bill', '=', False)]}"/> @@ -103,6 +102,9 @@ <field name="date_finance" readonly="1"/> <field name="date_logistic" readonly="1"/> </group> + <group> + <field name="bill_id" readonly="1" attrs="{'invisible': [('is_has_bill', '=', False)]}"/> + </group> </group> <!-- Product Lines --> <notebook> |
