diff options
20 files changed, 258 insertions, 127 deletions
diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 6212664e..e36b9920 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -461,7 +461,7 @@ class AccountMove(models.Model): 'finance@indoteknik.co.id', 'akbar@indoteknik.co.id', 'stephan@indoteknik.co.id', - 'darren@indoteknik.co.id' + # 'darren@indoteknik.co.id' ] sales_email = invs[0].invoice_user_id.partner_id.email if invs[0].invoice_user_id else None if sales_email and sales_email not in cc_list: diff --git a/indoteknik_custom/models/account_move_due_extension.py b/indoteknik_custom/models/account_move_due_extension.py index 7faad418..828be824 100644 --- a/indoteknik_custom/models/account_move_due_extension.py +++ b/indoteknik_custom/models/account_move_due_extension.py @@ -195,6 +195,7 @@ class DueExtensionLine(models.Model): invoice_id = fields.Many2one('account.move', string='Invoice') date_invoice = fields.Date(string='Invoice Date') efaktur_id = fields.Many2one('vit.efaktur', string='Faktur Pajak') + user_id = fields.Many2one('res.users', string='Salesperson', related='invoice_id.invoice_user_id') reference = fields.Char(string='Reference') total_amt = fields.Float(string='Total Amount') open_amt = fields.Float(string='Open Amount') @@ -215,7 +216,8 @@ class DueExtensionLine(models.Model): def _compute_day_to_due(self): for line in self: - line.day_to_due = line.invoice_id.invoice_day_to_due + # line.day_to_due = line.invoice_id.invoice_day_to_due + line.day_to_due = line.invoice_id.new_invoice_day_to_due def _compute_due_date(self): for line in self: diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index ec23de63..a50fabd7 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -205,6 +205,13 @@ class AdvancePaymentRequest(models.Model): self.account_name = self.env.user.id self.applicant_name = self.env.user.id + @api.constrains('is_represented', 'applicant_name', 'account_name', 'user_id') + def _check_applicant_consistency(self): + for rec in self: + if not rec.is_represented: + if rec.applicant_name != rec.user_id or rec.account_name != rec.user_id: + raise ValidationError("Nama Pemohon harus sesuai dengan User yang sedang Login, centang 'Nama Pemohon Berbeda?' jika ingin mewakilkan pemohon lain.") + @api.onchange('nominal') def _onchange_nominal_no_minus(self): if self.nominal and self.nominal < 0: @@ -481,15 +488,7 @@ class AdvancePaymentRequest(models.Model): 'target': 'current', 'context': { 'default_pum_id': self.id, - # 'default_value_down_payment': self.nominal, 'default_name': f'Realisasi - {self.number or ""}', - # 'default_pemberian_line_ids': [ - # (0, 0, { - # 'date': self.create_date.date() if self.create_date else fields.Date.today(), - # 'description': 'Uang Muka', - # 'value': self.nominal - # }) - # ] } } @@ -823,6 +822,9 @@ class AdvancePaymentUsageLine(models.Model): _name = 'advance.payment.usage.line' _description = 'Advance Payment Usage Line' + _order = 'sequence, id' + sequence = fields.Integer(string='Sequence', default=10) + realization_id = fields.Many2one('advance.payment.settlement', string='Realization') date = fields.Date(string='Tanggal', required=True, default=fields.Date.today) description = fields.Text(string='Description', required=True) @@ -903,7 +905,10 @@ class AdvancePaymentUsageLine(models.Model): class ReimburseLine(models.Model): _name = 'reimburse.line' - _description = 'Reimburse Line' + _description = 'Reimburse Line' + + _order = 'sequence, id' + sequence = fields.Integer(string='Sequence', default=10) request_id = fields.Many2one('advance.payment.request', string='Request') date = fields.Date(string='Tanggal', required=True, default=fields.Date.today) @@ -1325,7 +1330,10 @@ class AdvancePaymentSettlement(models.Model): rec.approved_by = (rec.approved_by + ', ' if rec.approved_by else '') + rec.name_approval_ap rec.date_approved_ap = now rec.position_ap = 'Finance AP' - rec.status = 'pengajuan3' + if rec.pum_id.position_type == 'pimpinan': + rec.status = 'approved' + else: + rec.status = 'pengajuan3' rec.message_post( body=f"Approval <b>AP</b> oleh <b>{self.env.user.name}</b> " @@ -1372,7 +1380,7 @@ class AdvancePaymentSettlement(models.Model): # now = fields.Datetime.now() # --- BATAS PENYESUAIAN --- - pum_id = vals.get('pum_id') + pum_id = vals.get('pum_id') or self._context.get('default_pum_id') initial_status = '' if pum_id: pum_request = self.env['advance.payment.request'].browse(pum_id) @@ -1516,7 +1524,7 @@ class CreateReimburseCabWizard(models.TransientModel): lines.append((0, 0, { 'account_id': line.account_id.id, 'partner_id': partner_id, - 'name': line.description, + 'name': f"{line.description} ({line.date})", 'debit': line.total, 'credit': 0, })) diff --git a/indoteknik_custom/models/coretax_fatur.py b/indoteknik_custom/models/coretax_fatur.py index cabcd5d6..c098fd01 100644 --- a/indoteknik_custom/models/coretax_fatur.py +++ b/indoteknik_custom/models/coretax_fatur.py @@ -117,7 +117,7 @@ class CoretaxFaktur(models.Model): ) # Calculate totals - total_product_amount = sum(line.get('price_subtotal', 0) if isinstance(line, dict) + total_product_amount = sum(line.get('price_subtotal', 0) if isinstance(line, dict) else line.price_subtotal for line in product_lines) if total_product_amount == 0: total_product_amount = 1 # Avoid division by zero @@ -147,7 +147,15 @@ class CoretaxFaktur(models.Model): subtotal = line_price_subtotal quantity = line_quantity total_discount = round(line_discount, 2) - coretax_id = line.product_uom_id.coretax_id + if isinstance(line, dict): + product = line.get('product_id') + uom = product.uom_id if product else False + else: + uom = line.product_uom_id + + coretax_id = uom.coretax_id if uom else 'UM.0018' + + # coretax_id = line.product_uom_id.coretax_id # Calculate other tax values otherTaxBase = round(subtotal * (11 / 12), 2) if subtotal else 0 vat_amount = round(otherTaxBase * 0.12, 2) @@ -159,7 +167,7 @@ class CoretaxFaktur(models.Model): ET.SubElement(good_service, 'Opt').text = 'A' ET.SubElement(good_service, 'Code').text = '000000' ET.SubElement(good_service, 'Name').text = line_name - ET.SubElement(good_service, 'Unit').text = coretax_id + ET.SubElement(good_service, 'Unit').text = coretax_id if invoice.partner_id.id != 80007 else 'UM.0018' # ET.SubElement(good_service, 'Price').text = str(round(line_price_unit, 2)) if line_price_unit else '0' ET.SubElement(good_service, 'Price').text = str(price_per_unit) ET.SubElement(good_service, 'Qty').text = str(quantity) diff --git a/indoteknik_custom/models/ged.py b/indoteknik_custom/models/ged.py index d438a065..38627fec 100644 --- a/indoteknik_custom/models/ged.py +++ b/indoteknik_custom/models/ged.py @@ -309,8 +309,9 @@ class DunningRunGed(models.Model): if ged_tracking: dunning.last_status_awb = ged_tracking.last_status if ged_tracking.last_status == 'POD Return': - dunning.date_terima_tukar_faktur = ged_tracking.delivered_date - dunning.copy_date_faktur() + if not dunning.date_terima_tukar_faktur: + dunning.date_terima_tukar_faktur = ged_tracking.delivered_date + dunning.copy_date_faktur() return def _get_tracking_history_log(self, awb_number='all_data'): diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 3312e7fd..3d22b0f0 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -123,6 +123,8 @@ class PurchaseOrder(models.Model): default=True ) + overseas_po = fields.Boolean(string='PO Luar Negeri?', tracking=3, help='Centang jika PO untuk pembelian luar negeri') + @staticmethod def is_local_env(): hostname = socket.gethostname().lower() @@ -1150,7 +1152,7 @@ class PurchaseOrder(models.Model): if self.is_local_env(): _logger.warning("📪 Local environment detected — skip sending email reminders.") return - self._send_mail() + # self._send_mail() if self.revisi_po: delta_time = current_time - timedelta(days=1) @@ -1227,58 +1229,58 @@ class PurchaseOrder(models.Model): 'order_line_id': line.id, }]) - def _send_mail(self): - output = io.BytesIO() - workbook = xlsxwriter.Workbook(output, {'in_memory': True}) - worksheet = workbook.add_worksheet() + # def _send_mail(self): + # output = io.BytesIO() + # workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + # worksheet = workbook.add_worksheet() - format6 = workbook.add_format({'font_size': 12, 'align': 'center', 'bg_color': '#D3D3D3', 'bold': True}) - format1 = workbook.add_format({'font_size': 11, 'align': 'center', 'valign': 'vcenter'}) + # format6 = workbook.add_format({'font_size': 12, 'align': 'center', 'bg_color': '#D3D3D3', 'bold': True}) + # format1 = workbook.add_format({'font_size': 11, 'align': 'center', 'valign': 'vcenter'}) - worksheet.set_column(0, 0, 10) - worksheet.set_column(1, 1, 20) - worksheet.set_column(2, 2, 20) - worksheet.set_column(3, 3, 20) - worksheet.set_column(4, 4, 15) - worksheet.set_column(5, 5, 15) + # worksheet.set_column(0, 0, 10) + # worksheet.set_column(1, 1, 20) + # worksheet.set_column(2, 2, 20) + # worksheet.set_column(3, 3, 20) + # worksheet.set_column(4, 4, 15) + # worksheet.set_column(5, 5, 15) - worksheet.write('A1', 'PO', format6) - worksheet.write('B1', 'SKU', format6) - worksheet.write('C1', 'Product', format6) - worksheet.write('D1', 'Brand', format6) - worksheet.write('E1', 'PO Price', format6) - worksheet.write('F1', 'Purchase Pricelist', format6) - worksheet.write('G1', 'Created On', format6) + # worksheet.write('A1', 'PO', format6) + # worksheet.write('B1', 'SKU', format6) + # worksheet.write('C1', 'Product', format6) + # worksheet.write('D1', 'Brand', format6) + # worksheet.write('E1', 'PO Price', format6) + # worksheet.write('F1', 'Purchase Pricelist', format6) + # worksheet.write('G1', 'Created On', format6) - row_number = 1 - po_sync = self.env['po.sync.price'].search([], order='create_date desc') - for po in po_sync: - worksheet.write(row_number, 0, po.order_line_id.order_id.name, format1) - worksheet.write(row_number, 1, po.order_line_id.product_id.default_code, format1) - worksheet.write(row_number, 2, po.order_line_id.product_id.name, format1) - worksheet.write(row_number, 3, po.order_line_id.product_id.x_manufacture.x_name, format1) - worksheet.write(row_number, 4, po.order_line_id.price_unit, format1) - worksheet.write(row_number, 5, po.order_line_id.price_vendor, format1) - worksheet.write(row_number, 6, po.create_date.replace(tzinfo=utc).astimezone(timezone('Asia/Jakarta')).strftime('%Y-%m-%d %H:%M:%S'), format1) - row_number += 1 - - workbook.close() + # row_number = 1 + # po_sync = self.env['po.sync.price'].search([], order='create_date desc') + # for po in po_sync: + # worksheet.write(row_number, 0, po.order_line_id.order_id.name, format1) + # worksheet.write(row_number, 1, po.order_line_id.product_id.default_code, format1) + # worksheet.write(row_number, 2, po.order_line_id.product_id.name, format1) + # worksheet.write(row_number, 3, po.order_line_id.product_id.x_manufacture.x_name, format1) + # worksheet.write(row_number, 4, po.order_line_id.price_unit, format1) + # worksheet.write(row_number, 5, po.order_line_id.price_vendor, format1) + # worksheet.write(row_number, 6, po.create_date.replace(tzinfo=utc).astimezone(timezone('Asia/Jakarta')).strftime('%Y-%m-%d %H:%M:%S'), format1) + # row_number += 1 + + # workbook.close() - output.seek(0) - - template = self.env.ref('indoteknik_custom.mail_template_po_sync_price') - template.attachment_ids.unlink() - attachment_vals = { - 'name': 'Purchase Order.xlsx', - 'datas': base64.b64encode(output.read()), - 'mimetype': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'res_model': 'mail.template', - 'res_id': template.id, - } - attachment_id = self.env['ir.attachment'].create(attachment_vals) - - template.attachment_ids = [(4, attachment_id.id)] - template.send_mail(self.id, force_send=True) + # output.seek(0) + + # template = self.env.ref('indoteknik_custom.mail_template_po_sync_price') + # template.attachment_ids.unlink() + # attachment_vals = { + # 'name': 'Purchase Order.xlsx', + # 'datas': base64.b64encode(output.read()), + # 'mimetype': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + # 'res_model': 'mail.template', + # 'res_id': template.id, + # } + # attachment_id = self.env['ir.attachment'].create(attachment_vals) + + # template.attachment_ids = [(4, attachment_id.id)] + # template.send_mail(self.id, force_send=True) def po_approve(self): greater_than_plafon, message = self._get_msg_plafon_qty() diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 2ed4046f..b108f544 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2780,8 +2780,9 @@ class SaleOrder(models.Model): salesperson_id = self.user_id.id approver_id = user.id team_leader_id = self.team_id.user_id.id + team = self.env['crm.team'].search([('user_id', '=', approver_id)], limit=1) - return salesperson_id == approver_id or approver_id == team_leader_id + return salesperson_id == approver_id or bool(team) def _create_approval_notification(self, approval_role): diff --git a/indoteknik_custom/models/stock_inventory.py b/indoteknik_custom/models/stock_inventory.py index 69cca5bc..e34d9a8e 100644 --- a/indoteknik_custom/models/stock_inventory.py +++ b/indoteknik_custom/models/stock_inventory.py @@ -52,6 +52,10 @@ class StockInventory(models.Model): return "00001" # Jika format tidak valid, mulai dari 00001 return "00001" # Jika belum ada data, mulai dari 00001 + def action_start(self): + if self.env.user.id != 21: + raise UserError("Hanya Rafly H. yang bisa start inventory") + @api.model def create(self, vals): """Pastikan nomor hanya dibuat saat penyimpanan.""" diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index e7686b75..900529b9 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1406,6 +1406,47 @@ class StockPicking(models.Model): else: raise UserError('Hanya MD yang bisa Approve') + def validate_seq_vcm(self): + for picking in self: + tg_po = picking.tukar_guling_po_id + if not tg_po: + continue + + return_type = tg_po.return_type + if return_type not in ['retur_po', 'tukar_guling']: + continue + + picking_order = [] + if return_type == 'retur_po': + picking_order = ['BU/INPUT', 'BU/PUT'] + elif return_type == 'tukar_guling': + picking_order = ['BU/VRT', 'BU/PRT', 'BU/INPUT', 'BU/PUT'] + + related_pickings = self.env['stock.picking'].search([ + ('tukar_guling_po_id', '=', tg_po.id) + ]) + + ordered_pickings = [] + for prefix in picking_order: + match = next((p for p in related_pickings if p.name.startswith(prefix)), None) + if not match: + raise UserError(f"Picking dengan prefix {prefix} belum ada.") + ordered_pickings.append(match) + + current_index = -1 + for idx, p in enumerate(ordered_pickings): + if p.id == picking.id: + current_index = idx + break + + if current_index == -1: + raise UserError("Picking ini tidak ditemukan dalam urutan picking yang sesuai.") + + for prev_picking in ordered_pickings[:current_index]: + if prev_picking.state != 'done': + raise UserError( + f"Tidak bisa validasi {picking.name} sebelum {prev_picking.name} divalidasi." + ) def button_validate(self): self.check_invoice_date() _logger.info("Kode Picking: %s", self.picking_type_id.code) @@ -1416,6 +1457,7 @@ class StockPicking(models.Model): group_id = self.env.ref('indoteknik_custom.group_role_merchandiser').id users_in_group = self.env['res.users'].search([('groups_id', 'in', [group_id])]) active_model = self.env.context.get('active_model') + self.validate_seq_vcm() if self.is_so_fiktif == True: raise UserError("SO Fiktif tidak bisa di validate") if self.location_id.id == 47 and self.env.user.id not in users_in_group.mapped( @@ -1453,10 +1495,10 @@ class StockPicking(models.Model): if not self.linked_manual_bu_out and 'BU/PICK/' in self.name: raise UserError(_("Isi BU Out terlebih dahulu!")) - if len(self.check_product_lines) == 0 and 'BU/PICK/' in self.name: + if len(self.check_product_lines) == 0 and 'BU/PICK/' in self.name and not self.env.user.has_group('indoteknik_custom.group_role_it'): raise UserError(_("Tidak ada Check Product! Harap periksa kembali.")) - if len(self.check_product_lines) == 0 and 'BU/INPUT/' in self.name: + if len(self.check_product_lines) == 0 and 'BU/INPUT/' in self.name and not self.env.user.has_group('indoteknik_custom.group_role_it'): raise UserError(_("Tidak ada Check Product! Harap periksa kembali.")) if self.total_koli > self.total_so_koli: @@ -2821,4 +2863,4 @@ class StockPickingSjDocument(models.Model): picking_id = fields.Many2one('stock.picking', required=True, ondelete='cascade') image = fields.Binary('Gambar', required=True, attachment=True) - sequence = fields.Integer('Urutan', default=10)
\ No newline at end of file + sequence = fields.Integer('Urutan', default=10) diff --git a/indoteknik_custom/models/stock_picking_return.py b/indoteknik_custom/models/stock_picking_return.py index 53a85f67..9cf017f9 100644 --- a/indoteknik_custom/models/stock_picking_return.py +++ b/indoteknik_custom/models/stock_picking_return.py @@ -120,8 +120,19 @@ class ReturnPicking(models.TransientModel): 'target': 'current', 'context': context, } - if picking.sale_id or 'SO' in (picking.origin or ''): - _logger.info("This picking is NOT from a PO, fallback to SO.") + # if picking.sale_id or 'SO' in (picking.origin or ''): + # _logger.info("This picking is NOT from a PO, fallback to SO.") + # return { + # 'name': _('Tukar Guling SO'), + # 'type': 'ir.actions.act_window', + # 'res_model': 'tukar.guling', + # 'view_mode': 'form', + # 'target': 'current', + # 'context': context, + # } + + if picking.name and any(k in picking.name.upper() for k in ('PICK', 'OUT')): + _logger.info("Redirect ke Tukar Guling SO via sale_id / origin") return { 'name': _('Tukar Guling SO'), 'type': 'ir.actions.act_window', diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index aa116ce3..f31c68d0 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -2,6 +2,7 @@ from odoo import models, fields, api, _ from odoo.exceptions import UserError, ValidationError import logging from datetime import datetime +from collections import defaultdict _logger = logging.getLogger(__name__) @@ -91,7 +92,7 @@ class TukarGuling(models.Model): def _compute_origin_so(self): for rec in self: rec.origin_so = False - origin_str = rec.origin or rec.operations.origin + origin_str = rec.operations.so_num if origin_str: so = self.env['sale.order'].search([('name', '=', origin_str)], limit=1) rec.origin_so = so.id if so else False @@ -425,12 +426,8 @@ class TukarGuling(models.Model): if not self.return_type: raise UserError("Return Type harus diisi!") - # if operasi == 30 and self.operations.linked_manual_bu_out.state == 'done': - # raise UserError("❌ Tidak bisa retur BU/PICK karena BU/OUT sudah done") if operasi == 30 and pp == 'tukar_guling': raise UserError("❌ BU/PICK tidak boleh di retur tukar guling") - # else: - # _logger.info("hehhe") if 'operations' in vals and not vals.get('origin'): picking = self.env['stock.picking'].browse(vals['operations']) @@ -498,8 +495,6 @@ class TukarGuling(models.Model): ('state', '!=', 'cancel'), ], limit=1) - # if existing_tukar_guling: - # raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) picking = self.operations if picking.picking_type_id.id == 30 and self.return_type == 'tukar_guling': raise UserError("❌ BU/PICK tidak boleh di retur tukar guling") @@ -510,8 +505,6 @@ class TukarGuling(models.Model): linked_bu_out = picking.linked_manual_bu_out if linked_bu_out and linked_bu_out.state == 'done': raise UserError("❌ Tidak bisa retur BU/PICK karena BU/OUT suda Done!") - # if self._is_already_returned(self.operations): - # raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") if self.operations.picking_type_id.id == 29: # Cek apakah ada BU/PICK di origin @@ -521,16 +514,30 @@ class TukarGuling(models.Model): ('picking_type_id', '=', 30), ('state', '!=', 'cancel') ]) > 0 - if has_bu_pick: - for line in self.line_ids: - mapping_lines = self.mapping_koli_ids.filtered(lambda x: x.product_id == line.product_id) - total_qty = sum(l.qty_return for l in mapping_lines) - if total_qty != line.product_uom_qty: - raise UserError( - _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name - ) - # self._check_invoice_on_retur_so() + for record in self: + # Step 1: Hitung total qty per produk dari line_ids + product_qty_map = defaultdict(float) + for line in record.line_ids: + product_qty_map[line.product_id] += line.product_uom_qty + + # Step 2: Cek mapping koli + for product, total_line_qty in product_qty_map.items(): + mapping_lines = record.mapping_koli_ids.filtered(lambda x: x.product_id == product) + total_mapping_qty = sum(l.qty_return for l in mapping_lines) + + if total_mapping_qty != total_line_qty: + raise UserError( + _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % product.display_name + ) + # if has_bu_pick: + # for line in self.line_ids: + # mapping_lines = self.mapping_koli_ids.filtered(lambda x: x.product_id == line.product_id) + # total_qty = sum(l.qty_return for l in mapping_lines) + # if total_qty != line.product_uom_qty: + # raise UserError( + # _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name + # ) self._validate_product_lines() if self.state != 'draft': @@ -621,13 +628,28 @@ class TukarGuling(models.Model): ]) > 0 if has_bu_pick: - for line in self.line_ids: - mapping_lines = self.mapping_koli_ids.filtered(lambda x: x.product_id == line.product_id) - total_qty = sum(l.qty_return for l in mapping_lines) - if total_qty != line.product_uom_qty: - raise UserError( - _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name - ) + for record in self: + # Step 1: Hitung total qty per produk dari line_ids + product_qty_map = defaultdict(float) + for line in record.line_ids: + product_qty_map[line.product_id] += line.product_uom_qty + + # Step 2: Cek mapping koli + for product, total_line_qty in product_qty_map.items(): + mapping_lines = record.mapping_koli_ids.filtered(lambda x: x.product_id == product) + total_mapping_qty = sum(l.qty_return for l in mapping_lines) + + if total_mapping_qty != total_line_qty: + raise UserError( + _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % product.display_name + ) + # for line in self.line_ids: + # mapping_lines = self.mapping_koli_ids.filtered(lambda x: x.product_id == line.product_id) + # total_qty = sum(l.qty_return for l in mapping_lines) + # if total_qty != line.product_uom_qty: + # raise UserError( + # _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name + # ) if operasi == 30 and self.operations.linked_manual_bu_out.state == 'done': raise UserError("❌ Tidak bisa retur BU/PICK karena BU/OUT sudah done") @@ -737,17 +759,16 @@ class TukarGuling(models.Model): if mapping_koli and record.operations.picking_type_id.id == 29: for prod in mapping_koli.mapped('product_id'): qty_total = sum(mk.qty_return for mk in mapping_koli.filtered(lambda m: m.product_id == prod)) - - move_lines = bu_out.move_line_ids.filtered( - lambda ml: ml.product_id == prod and ml.qty_done > 0 and not ml.package_id - ) - - for ml in move_lines: - srt_return_lines.append((0, 0, { - 'product_id': ml.product_id.id, - 'quantity': ml.qty_done, - 'move_id': ml.move_id.id, - })) + if qty_total == 0: + continue + move = bu_out.move_lines.filtered(lambda m: m.product_id == prod) + if not move: + raise UserError(f"Move BU/OUT tidak ditemukan untuk produk {prod.display_name}") + srt_return_lines.append((0, 0, { + 'product_id': prod.id, + 'quantity': qty_total, + 'move_id': move[0].id, + })) _logger.info(f"📟 SRT line: {prod.display_name} | qty={qty_total}") @@ -1038,4 +1059,4 @@ class TukarGulingMappingKoli(models.Model): for rec in self: if rec.tukar_guling_id and rec.tukar_guling_id.state not in ['draft', 'cancel']: raise UserError("Tidak bisa menghapus Mapping Koli karena status Tukar Guling bukan Draft atau Cancel.") - return super(TukarGulingMappingKoli, self).unlink()
\ No newline at end of file + return super(TukarGulingMappingKoli, self).unlink() diff --git a/indoteknik_custom/report/purchase_report.xml b/indoteknik_custom/report/purchase_report.xml index 208e6472..d3509720 100644 --- a/indoteknik_custom/report/purchase_report.xml +++ b/indoteknik_custom/report/purchase_report.xml @@ -61,9 +61,17 @@ <td style="width:50%; border:1px solid #ccc; border-radius:8px; padding:10px; background:#fcfcfc; vertical-align:top;"> <strong style="color:#d32f2f;">Alamat Pengiriman</strong><br/> PT. Indoteknik Dotcom Gemilang<br/> - Jl. Bandengan Utara Komp A 8 B - RT. Penjaringan, Kec. Penjaringan, Jakarta - (BELAKANG INDOMARET)<br/> + <t t-if="doc.overseas_po == True"> + JALAN BANDENGAN UTARA 85A NO.8-9 RT. 003<br/> + RW. 016, PENJARINGAN, PENJARINGAN, KOTA<br/> + ADM. JAKARTA UTARA, DKI JAKARTA + </t> + <t t-else=""> + Jl. Bandengan Utara Komp A 8 B RT.<br/> + Penjaringan, Kec. Penjaringan, Jakarta<br/> + (BELAKANG INDOMARET) + </t> + <br/> Daerah Khusus Ibukota Jakarta 14440 </td> <td style="width:50%; border:1px solid #ccc; border-radius:8px; padding:10px; background:#fcfcfc; vertical-align:top;"> @@ -97,10 +105,16 @@ <!-- TEKS --> <div style="display:flex; flex-direction:column; flex:1;"> <span style="font-weight:bold; margin-bottom:2px;"> - <t t-esc="line_index + 1"/>. <t t-esc="line.name"/> - <!-- <t t-esc="line_index + 1"/>. <t t-esc="line.product_id.display_name"/> --> + <t t-esc="line_index + 1"/>. + <t t-if="line.product_id.id == 114360"> + <t t-esc="line.name"/> + </t> + <t t-else=""> + <t t-esc="line.product_id.display_name"/> + </t> </span> </div> + </td> <td style="border:1px solid #ccc; padding:6px; text-align:center;"> diff --git a/indoteknik_custom/views/account_move_line.xml b/indoteknik_custom/views/account_move_line.xml index 3a20388e..cb24a0f0 100644 --- a/indoteknik_custom/views/account_move_line.xml +++ b/indoteknik_custom/views/account_move_line.xml @@ -16,6 +16,9 @@ <attribute name="groups"/> <attribute name="options">{'no_create': True}</attribute> </xpath> + <xpath expr="//field[@name='line_ids']/tree/field[@name='partner_id']" position="attributes"> + <attribute name="options">{'no_create': True, 'no_open': True}</attribute> + </xpath> </field> </record> </data> diff --git a/indoteknik_custom/views/account_move_views.xml b/indoteknik_custom/views/account_move_views.xml index 08b93f1f..b226460c 100644 --- a/indoteknik_custom/views/account_move_views.xml +++ b/indoteknik_custom/views/account_move_views.xml @@ -30,7 +30,7 @@ <field name="date_invoice"/> <field name="due_date"/> <field name="day_to_due"/> - <field name="efaktur_id"/> + <field name="user_id" widget="many2one_avatar_user"/> <field name="reference"/> <field name="total_amt"/> <field name="open_amt" sum="Grand Total Open Amount"/> diff --git a/indoteknik_custom/views/advance_payment_request.xml b/indoteknik_custom/views/advance_payment_request.xml index 4e73bb28..278deda5 100644 --- a/indoteknik_custom/views/advance_payment_request.xml +++ b/indoteknik_custom/views/advance_payment_request.xml @@ -86,12 +86,13 @@ <group string=" "> <field name="type_request" attrs="{'readonly': [('status', '=', 'approved')]}"/> <field name="is_represented" attrs="{'readonly': [('status', '=', 'approved')]}"/> - <field name="applicant_name" colspan="2" attrs="{'readonly': [('status', '=', 'approved')]}"/> + <p style="font-size: 11px; color: grey; font-style: italic" >*Centang jika Anda membuat pengajuan ini untuk orang lain.</p> + <field name="applicant_name" colspan="2" attrs="{'readonly': ['|', ('status', '=', 'approved'), ('is_represented', '=', False)]}" force_save="1"/> <field name="position_type" force_save="1" readonly="1"/> <field name="nominal" colspan="2" attrs="{'readonly': ['|', ('status', '=', 'approved'), ('type_request', '=', 'reimburse')]}" force_save="1"/> - <p style="font-size: 10px; color: grey; font-style: italic" attrs="{'invisible': [('type_request', '!=', 'reimburse')]}">*Nominal terisi otomatis sesuai grand total rincian reimburse</p> + <p style="font-size: 11px; color: grey; font-style: italic" attrs="{'invisible': [('type_request', '!=', 'reimburse')]}">*Nominal terisi otomatis sesuai grand total rincian reimburse</p> <field name="bank_name" colspan="2" attrs="{'readonly': [('status', '=', 'approved')]}"/> - <field name="account_name" colspan="2" attrs="{'readonly': [('status', '=', 'approved')]}"/> + <field name="account_name" colspan="2" attrs="{'readonly': ['|', ('status', '=', 'approved'), ('is_represented', '=', False)]}" force_save="1"/> <field name="bank_account" colspan="2" attrs="{'readonly': [('status', '=', 'approved')]}"/> <field name="detail_note" attrs="{'readonly': [('status', '=', 'approved')]}"/> <br/> @@ -130,6 +131,7 @@ <page string="Rincian Reimburse"> <field name="reimburse_line_ids"> <tree> + <field name="sequence" widget="handle"/> <field name="date"/> <field name="description"/> <field name="account_id"/> @@ -219,6 +221,7 @@ <field name="model">advance.payment.request</field> <field name="arch" type="xml"> <search string="Search APR & Reimburse"> + <field name="number" string="No. Dokumen"/> <filter string="My Requests" name="my_requests" domain="[('applicant_name','=',uid)]"/> <separator/> <filter string="PUM" name="filter_pum" domain="[('type_request','=','pum')]"/> diff --git a/indoteknik_custom/views/advance_payment_settlement.xml b/indoteknik_custom/views/advance_payment_settlement.xml index 050e3933..a77baffe 100644 --- a/indoteknik_custom/views/advance_payment_settlement.xml +++ b/indoteknik_custom/views/advance_payment_settlement.xml @@ -74,6 +74,7 @@ <page string="Rincian Penggunaan"> <field name="penggunaan_line_ids" nolabel="1"> <tree> + <field name="sequence" widget="handle"/> <field name="date"/> <field name="description"/> <field name="nominal" sum="Total Penggunaan"/> diff --git a/indoteknik_custom/views/dunning_run.xml b/indoteknik_custom/views/dunning_run.xml index 911a372d..52a0c6ae 100644 --- a/indoteknik_custom/views/dunning_run.xml +++ b/indoteknik_custom/views/dunning_run.xml @@ -25,8 +25,8 @@ <field name="arch" type="xml"> <tree> <field name="partner_id"/> - <field name="information_line"/> - <field name="reference"/> + <field name="information_line" optional="hide"/> + <field name="reference" optional="hide"/> <field name="invoice_id"/> <field name="date_invoice"/> <field name="efaktur_id" optional="hide"/> @@ -125,4 +125,4 @@ action="dunning_run_action" parent="account.menu_finance_reports" sequence="200"/> -</odoo>
\ No newline at end of file +</odoo> diff --git a/indoteknik_custom/views/mail_template_po.xml b/indoteknik_custom/views/mail_template_po.xml index edbff609..9163d941 100644 --- a/indoteknik_custom/views/mail_template_po.xml +++ b/indoteknik_custom/views/mail_template_po.xml @@ -6,7 +6,8 @@ <field name="model_id" ref="model_purchase_order" /> <field name="subject">Your PO ${object.name}</field> <field name="email_from"></field> - <field name="email_to">darren@indoteknik.co.id, azkan4elll@gmail.com</field> + <!-- <field name="email_to">darren@indoteknik.co.id, azkan4elll@gmail.com</field> --> + <field name="email_to">azkan4elll@gmail.com</field> <field name="body_html" type="html"> <table border="0" cellpadding="0" cellspacing="0" style="padding-top: 16px; background-color: #F1F1F1; font-family:Verdana, Arial,sans-serif; color: #454748; width: 100%; border-collapse:separate;"> diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml index 09d901b9..16b8bd44 100755 --- a/indoteknik_custom/views/purchase_order.xml +++ b/indoteknik_custom/views/purchase_order.xml @@ -179,6 +179,7 @@ <field name="from_apo"/> <field name="approval_edit_line"/> <field name="logbook_bill_id"/> + <field name="overseas_po"/> <field name="bills_dp_id" readonly="1"/> <field name="bills_pelunasan_id" readonly="1"/> </field> diff --git a/indoteknik_custom/views/unpaid_invoice_view.xml b/indoteknik_custom/views/unpaid_invoice_view.xml index ec6c749d..86b15a07 100644 --- a/indoteknik_custom/views/unpaid_invoice_view.xml +++ b/indoteknik_custom/views/unpaid_invoice_view.xml @@ -13,10 +13,10 @@ <field name="date_terima_tukar_faktur"/> <field name="payment_term_id"/> <field name="invoice_date_due" widget="badge" decoration-danger="invoice_day_to_due < 0"/> - <field name="invoice_day_to_due" readonly="1"/> - <field name="new_invoice_day_to_due" readonly="1"/> - <field name="amount_total"/> - <field name="amount_residual"/> + <field name="invoice_day_to_due" widget="badge" decoration-danger="invoice_day_to_due < 0" readonly="1"/> + <field name="new_invoice_day_to_due" widget="badge" decoration-danger="invoice_day_to_due < 0" readonly="1"/> + <field name="amount_total" sum="Total Amount"/> + <field name="amount_residual" sum="Sisa Amount"/> <field name="payment_state" widget="badge" decoration-danger="payment_state == 'not_paid'" decoration-warning="payment_state == 'partial'"/> @@ -79,6 +79,14 @@ <search string="Search Unpaid Invoices"> <field name="partner_id"/> <field name="invoice_number"/> + <separator/> + <filter name="overdue" string="Overdue" domain="[('new_invoice_day_to_due', '<', 0)]"/> + <filter name="today" string="Today" domain="[('new_invoice_day_to_due', '=', 0)]"/> + <filter name="due" string="Due" domain="[('new_invoice_day_to_due', '<', 0)]"/> + <separator/> + <group expand="0" string="Group By"> + <filter string="Partner" name="group_partner" context="{'group_by': 'partner_id'}"/> + </group> </search> </field> </record> |
