diff options
| author | trisusilo48 <tri.susilo@altama.co.id> | 2025-04-24 15:21:02 +0700 |
|---|---|---|
| committer | trisusilo48 <tri.susilo@altama.co.id> | 2025-04-24 15:21:02 +0700 |
| commit | e8938477ca3f87a55b0e4ca313481fe8d7e8fef4 (patch) | |
| tree | 82839a121578f00a40dcd129b90815c70840865a | |
| parent | d9d8b9f3afc0ad60ca1199b08ab6e2836663a0de (diff) | |
| parent | 2db8d058d5b7c291669240df90afc0312d509939 (diff) | |
Merge branch 'odoo-backup' into feature/feedback_bitehisp
# Conflicts:
# indoteknik_custom/models/__init__.py
22 files changed, 1106 insertions, 365 deletions
diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index abd78f0e..e16af332 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -116,10 +116,10 @@ class StockPicking(controller.Controller): return self.response(picking.get_tracking_detail()) - @http.route(prefix + 'stock-picking/<id>/documentation', auth='public', methods=['PUT', 'OPTIONS'], csrf=False) + @http.route(prefix + 'stock-picking/<scanid>/documentation', auth='public', methods=['PUT', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() def write_partner_stock_picking_documentation(self, **kw): - id = int(kw.get('id', 0)) + scanid = int(kw.get('scanid', 0)) sj_document = kw.get('sj_document', False) paket_document = kw.get('paket_document', False) @@ -128,7 +128,10 @@ class StockPicking(controller.Controller): 'driver_arrival_date': datetime.utcnow(), } - picking_data = request.env['stock.picking'].search([('id', '=', id)], limit=1) + picking_data = request.env['stock.picking'].search([('id', '=', scanid)], limit=1) + + if not picking_data: + picking_data = request.env['stock.picking'].search([('picking_code', '=', scanid)], limit=1) if not picking_data: return self.response(code=404, description='picking not found') diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index eb00f527..dec0f26a 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -143,6 +143,7 @@ 'views/sale_order_multi_uangmuka_penjualan.xml', 'views/shipment_group.xml', 'views/approval_date_doc.xml', + 'views/approval_invoice_date.xml', 'views/partner_payment_term.xml', 'views/vendor_payment_term.xml', 'views/approval_unreserve.xml', diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index 06af8b61..605d1016 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -150,3 +150,4 @@ from . import stock_backorder_confirmation from . import account_payment_register from . import stock_inventory from . import sale_order_delay +from . import approval_invoice_date diff --git a/indoteknik_custom/models/approval_date_doc.py b/indoteknik_custom/models/approval_date_doc.py index 751bae82..1a2749d5 100644 --- a/indoteknik_custom/models/approval_date_doc.py +++ b/indoteknik_custom/models/approval_date_doc.py @@ -39,12 +39,15 @@ class ApprovalDateDoc(models.Model): if not self.env.user.is_accounting: raise UserError("Hanya Accounting Yang Bisa Approve") self.check_invoice_so_picking - self.picking_id.driver_departure_date = self.driver_departure_date - self.picking_id.date_doc_kirim = self.driver_departure_date + # Tambahkan context saat mengupdate date_doc_kirim + self.picking_id.with_context(from_button_approve=True).write({ + 'driver_departure_date': self.driver_departure_date, + 'date_doc_kirim': self.driver_departure_date + }) self.state = 'done' self.approve_date = datetime.utcnow() self.approve_by = self.env.user.id - + def button_cancel(self): self.state = 'cancel' diff --git a/indoteknik_custom/models/approval_invoice_date.py b/indoteknik_custom/models/approval_invoice_date.py new file mode 100644 index 00000000..48546e55 --- /dev/null +++ b/indoteknik_custom/models/approval_invoice_date.py @@ -0,0 +1,46 @@ +from odoo import models, api, fields +from odoo.exceptions import AccessError, UserError, ValidationError +from datetime import timedelta, date, datetime +import logging + +_logger = logging.getLogger(__name__) + +class ApprovalInvoiceDate(models.Model): + _name = "approval.invoice.date" + _description = "Approval Invoice Date" + _rec_name = 'number' + + picking_id = fields.Many2one('stock.picking', string='Picking') + number = fields.Char(string='Document No', index=True, copy=False, readonly=True, tracking=True) + date_invoice = fields.Datetime( + string='Invoice Date', + copy=False + ) + date_doc_do = fields.Datetime( + string='Tanggal Kirim di SJ', + copy=False + ) + state = fields.Selection([('draft', 'Draft'), ('done', 'Done'), ('cancel', 'Cancel')], string='State', default='draft', tracking=True) + approve_date = fields.Datetime(string='Approve Date', copy=False) + approve_by = fields.Many2one('res.users', string='Approve By', copy=False) + sale_id = fields.Many2one('sale.order', string='Sale Order') + partner_id = fields.Many2one('res.partner', string='Partner') + move_id = fields.Many2one('account.move', string='Invoice') + note = fields.Char(string='Note') + + def button_approve(self): + if not self.env.user.is_accounting: + raise UserError("Hanya Accounting Yang Bisa Approve") + self.move_id.invoice_date = self.date_doc_do + self.state = 'done' + self.approve_date = datetime.utcnow() + self.approve_by = self.env.user.id + + def button_cancel(self): + self.state = 'cancel' + + @api.model + def create(self, vals): + vals['number'] = self.env['ir.sequence'].next_by_code('approval.invoice.date') or '0' + result = super(ApprovalInvoiceDate, self).create(vals) + return result diff --git a/indoteknik_custom/models/barcoding_product.py b/indoteknik_custom/models/barcoding_product.py index e1b8f41f..204c6128 100644 --- a/indoteknik_custom/models/barcoding_product.py +++ b/indoteknik_custom/models/barcoding_product.py @@ -12,15 +12,32 @@ class BarcodingProduct(models.Model): barcoding_product_line = fields.One2many('barcoding.product.line', 'barcoding_product_id', string='Barcoding Product Lines', auto_join=True) product_id = fields.Many2one('product.product', string="Product", tracking=3) quantity = fields.Float(string="Quantity", tracking=3) - type = fields.Selection([('print', 'Print Barcode'), ('barcoding', 'Add Barcode To Product')], string='Type', default='print') + type = fields.Selection([('print', 'Print Barcode'), ('barcoding', 'Add Barcode To Product'), ('barcoding_box', 'Add Barcode Box To Product')], string='Type', default='print') barcode = fields.Char(string="Barcode") + qty_pcs_box = fields.Char(string="Quantity Pcs Box") + + def check_duplicate_barcode(self): + if self.type in ['barcoding_box', 'barcoding']: + barcode_product = self.env['product.product'].search([('barcode', '=', self.barcode)]) + + if barcode_product: + raise UserError('Barcode sudah digunakan {}'.format(barcode_product.display_name)) + + barcode_box = self.env['product.product'].search([('barcode_box', '=', self.barcode)]) + + if barcode_box: + raise UserError('Barcode box sudah digunakan {}'.format(barcode_box.display_name)) @api.constrains('barcode') def _send_barcode_to_product(self): for record in self: - if record.barcode and not record.product_id.barcode: + record.check_duplicate_barcode() + if record.type == 'barcoding_box': + record.product_id.barcode_box = record.barcode + record.product_id.qty_pcs_box = record.qty_pcs_box + else: record.product_id.barcode = record.barcode - + @api.onchange('product_id', 'quantity') def _onchange_product_or_quantity(self): """Update barcoding_product_line based on product_id and quantity""" diff --git a/indoteknik_custom/models/delivery_order.py b/indoteknik_custom/models/delivery_order.py index 3473197b..2dd0c802 100644 --- a/indoteknik_custom/models/delivery_order.py +++ b/indoteknik_custom/models/delivery_order.py @@ -25,7 +25,8 @@ class DeliveryOrder(models.TransientModel): picking = False if delivery_order_line[2]['name']: picking = self.env['stock.picking'].search([('picking_code', '=', delivery_order_line[2]['name'])], limit=1) - + if not picking: + picking = self.env['stock.picking'].search([('out_code', '=', delivery_order_line[2]['name'])], limit=1) if picking: line_tracking_no = delivery_order_line[2]['tracking_no'] @@ -86,6 +87,10 @@ class DeliveryOrderLine(models.TransientModel): if len(self.name) == 13: self.name = self.name[:-1] picking = self.env['stock.picking'].search([('picking_code', '=', self.name)], limit=1) + + if not picking: + picking = self.env['stock.picking'].search([('out_code', '=', self.name)], limit=1) + if picking: if picking.driver_id: self.driver_id = picking.driver_id diff --git a/indoteknik_custom/models/logbook_sj.py b/indoteknik_custom/models/logbook_sj.py index 9f349882..75b2622f 100644 --- a/indoteknik_custom/models/logbook_sj.py +++ b/indoteknik_custom/models/logbook_sj.py @@ -26,6 +26,8 @@ class LogbookSJ(models.TransientModel): report_logbook = self.env['report.logbook.sj'].create([parameters_header]) for line in logbook_line: picking = self.env['stock.picking'].search([('picking_code', '=', line.name)], limit=1) + if not picking: + picking = self.env['stock.picking'].search([('out_code', '=', line.name)], limit=1) stock = picking parent_id = stock.partner_id.parent_id.id parent_id = parent_id if parent_id else stock.partner_id.id @@ -80,6 +82,9 @@ class LogbookSJLine(models.TransientModel): if len(self.name) == 13: self.name = self.name[:-1] picking = self.env['stock.picking'].search([('picking_code', '=', self.name)], limit=1) + + if not picking: + picking = self.env['stock.picking'].search([('out_code', '=', self.name)], limit=1) if picking: if picking.driver_id: self.driver_id = picking.driver_id diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index 600dd90e..e6a01a04 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -421,6 +421,8 @@ class ProductProduct(models.Model): plafon_qty = fields.Float(string='Max Plafon', compute='_get_plafon_qty_product') merchandise_ok = fields.Boolean(string='Product Promotion') qr_code_variant = fields.Binary("QR Code Variant", compute='_compute_qr_code_variant') + qty_pcs_box = fields.Float("Pcs Box") + barcode_box = fields.Char("Barcode Box") def generate_product_sla(self): product_variant_ids = self.env.context.get('active_ids', []) diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index b107f389..98b367d0 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -89,6 +89,112 @@ class PurchaseOrder(models.Model): store_name = fields.Char(string='Nama Toko') purchase_order_count = fields.Integer('Purchase Order Count', related='partner_id.purchase_order_count') + # cek payment term + def _check_payment_term(self): + _logger.info("Check Payment Term Terpanggil") + + cbd_term = self.env['account.payment.term'].search([ + ('name', 'ilike', 'Cash Before Delivery') + ], limit=1) + + for order in self: + if not order.partner_id or not order.partner_id.minimum_amount: + continue + + if not order.order_line or order.amount_total == 0: + continue + + if order.amount_total < order.partner_id.minimum_amount: + if cbd_term and order.payment_term_id != cbd_term: + order.payment_term_id = cbd_term.id + self.env.user.notify_info( + message="Total belanja PO belum mencapai minimum yang ditentukan vendor. " + "Payment Term telah otomatis diubah menjadi Cash Before Delivery (C.B.D).", + title="Payment Term Diperbarui" + ) + else: + vendor_term = order.partner_id.property_supplier_payment_term_id + if vendor_term and order.payment_term_id != vendor_term: + order.payment_term_id = vendor_term.id + self.env.user.notify_info( + message=f"Total belanja PO telah memenuhi jumlah minimum vendor. " + f"Payment Term otomatis dikembalikan ke pengaturan vendor awal: *{vendor_term.name}*.", + title="Payment Term Diperbarui" + ) + + def _check_tax_rule(self): + _logger.info("Check Tax Rule Terpanggil") + + # Pajak 11% + tax_11 = self.env['account.tax'].search([ + ('type_tax_use', '=', 'purchase'), + ('name', 'ilike', '11%') + ], limit=1) + + # Pajak "No Tax" + no_tax = self.env['account.tax'].search([ + ('type_tax_use', '=', 'purchase'), + ('name', 'ilike', 'no tax') + ], limit=1) + + if not tax_11: + raise UserError("Pajak 11% tidak ditemukan. Mohon pastikan pajak 11% tersedia.") + + if not no_tax: + raise UserError("Pajak 'No Tax' tidak ditemukan. Harap buat tax dengan nama 'No Tax' dan tipe 'Purchase'.") + + for order in self: + partner = order.partner_id + minimum_tax = partner.minimum_amount_tax + + _logger.info("Partner ID: %s, Minimum Tax: %s, Untaxed Total: %s", partner.id, minimum_tax, order.amount_untaxed) + + if not minimum_tax or not order.order_line: + continue + + if order.amount_total < minimum_tax: + _logger.info(">>> Total di bawah minimum → apply No Tax") + for line in order.order_line: + line.taxes_id = [(6, 0, [no_tax.id])] + + if self.env.context.get('notify_tax'): + self.env.user.notify_info( + message="Total belanja PO belum mencapai minimum pajak vendor. " + "Pajak diganti menjadi 'No Tax'.", + title="Pajak Diperbarui", + ) + else: + _logger.info(">>> Total memenuhi minimum → apply Pajak 11%") + for line in order.order_line: + line.taxes_id = [(6, 0, [tax_11.id])] + + if self.env.context.get('notify_tax'): + self.env.user.notify_info( + message="Total belanja sebelum pajak telah memenuhi minimum. " + "Pajak 11%% diterapkan", + title="Pajak Diperbarui", + ) + + # set default no_tax pada order line + # @api.onchange('order_line') + # def _onchange_order_line_tax_default(self): + # _logger.info("Onchange Order Line Tax Default Terpanggil") + + # no_tax = self.env['account.tax'].search([ + # ('type_tax_use', '=', 'purchase'), + # ('name', 'ilike', 'no tax') + # ], limit=1) + + # if not no_tax: + # _logger.info("No Tax tidak ditemukan") + # return + + # for order in self: + # for line in order.order_line: + # if not line.taxes_id: + # line.taxes_id = [(6, 0, [no_tax.id])] + # _logger.info("Auto-set No tax ke baris product: %s", line.product_id.name) + @api.onchange('total_cost_service') def _onchange_total_cost_service(self): for order in self: @@ -672,6 +778,7 @@ class PurchaseOrder(models.Model): raise UserError("Produk "+line.product_id.name+" memiliki vendor berbeda dengan SO (Vendor PO: "+str(self.partner_id.name)+", Vendor SO: "+str(line.so_line_id.vendor_id.name)+")") def button_confirm(self): + # self._check_payment_term() # check payment term res = super(PurchaseOrder, self).button_confirm() current_time = datetime.now() self.check_ppn_mix() @@ -1079,6 +1186,19 @@ class PurchaseOrder(models.Model): return super(PurchaseOrder, self).button_unlock() + @api.model #override custom create & write for check payment term + def create(self, vals): + order = super().create(vals) + # order.with_context(skip_check_payment=True)._check_payment_term() + # order.with_context(notify_tax=True)._check_tax_rule() + return order + + def write(self, vals): + res = super().write(vals) + if not self.env.context.get('skip_check_payment'): + self.with_context(skip_check_payment=True)._check_payment_term() + self.with_context(notify_tax=True)._check_tax_rule() + return res class PurchaseOrderUnlockWizard(models.TransientModel): _name = 'purchase.order.unlock.wizard' diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index fd3a0514..191a44c9 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -2,6 +2,7 @@ from odoo import models, fields, api from odoo.exceptions import UserError, ValidationError from datetime import datetime from odoo.http import request +import re class GroupPartner(models.Model): _name = 'group.partner' @@ -39,6 +40,14 @@ class ResPartner(models.Model): estimasi_tempo = fields.Char(string='Estimasi Pembelian Pertahun') tempo_duration = fields.Many2one('account.payment.term', string='Durasi Tempo') tempo_limit = fields.Char(string='Limit Tempo') + minimum_amount = fields.Float( + string="Minimum Order", + help="Jika total belanja kurang dari ini, maka payment term akan otomatis menjadi CBD." + ) + minimum_amount_tax = fields.Float( + string="Minimum Amount Tax", + help="Jika total belanja kurang dari ini, maka tax akan otomatis menjadi 0%." + ) category_produk_ids = fields.Many2many('product.public.category', string='Kategori Produk yang Digunakan', domain=lambda self: self._get_default_category_domain()) @api.model @@ -200,6 +209,32 @@ class ResPartner(models.Model): if existing_partner: raise ValidationError(f"Nama '{record.name}' sudah digunakan oleh partner lain!") + @api.constrains('npwp') + def _check_npwp(self): + for record in self: + npwp = record.npwp.strip() if record.npwp else '' + # Abaikan validasi jika NPWP kosong atau diisi "0" + if not npwp or npwp == '0' or npwp == '00.000.000.0-000.000': + continue + + # Validasi untuk NPWP 15 digit (format: 99.999.999.9-999.999) + if len(npwp) == 20: + # Regex untuk 15 digit dengan format titik dan tanda hubung + pattern_15_digit = r'^\d{2}\.\d{3}\.\d{3}\.\d{1}-\d{3}\.\d{3}$' + if not re.match(pattern_15_digit, npwp): + raise ValidationError("Format NPWP 15 digit yang dimasukkan salah. Pastikan format yang benar adalah: 99.999.999.9-999.999") + + # Validasi untuk NPWP 16 digit (hanya angka tanpa titik atau tanda hubung) + elif len(npwp) == 16: + pattern_16_digit = r'^\d{16}$' + if not re.match(pattern_16_digit, npwp): + raise ValidationError("Format NPWP 16 digit yang dimasukkan salah. Format yang benar adalah 16 digit angka tanpa titik atau tanda hubung.") + + # Validasi panjang NPWP jika lebih atau kurang dari 15 atau 16 digit + else: + raise ValidationError("Digit NPWP yang dimasukkan tidak sesuai. Pastikan NPWP memiliki 15 digit dengan format tertentu (99.999.999.9-999.999) atau 16 digit tanpa tanda hubung.") + + def write(self, vals): # Fungsi rekursif untuk meng-update semua child, termasuk child dari child def update_children_recursively(partner, vals_for_child): @@ -465,6 +500,8 @@ class ResPartner(models.Model): def _onchange_customer_type(self): if self.customer_type == 'nonpkp': self.npwp = '00.000.000.0-000.000' + elif self.customer_type == 'pkp': + self.npwp = '00.000.000.0-000.000' def get_check_payment_term(self): self.ensure_one() diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index a7ee9db8..3117a330 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -67,6 +67,17 @@ class ShippingOption(models.Model): class SaleOrder(models.Model): _inherit = "sale.order" + ongkir_ke_xpdc = fields.Float(string='Ongkir ke Ekspedisi', help='Biaya ongkir ekspedisi', copy=False, index=True, tracking=3) + + metode_kirim_ke_xpdc = fields.Selection([ + ('indoteknik_deliv', 'Indoteknik Delivery'), + ('lalamove', 'Lalamove'), + ('grab', 'Grab'), + ('gojek', 'Gojek'), + ('deliveree', 'Deliveree'), + ('other', 'Other'), + ], string='Metode Kirim Ke Ekspedisi', copy=False, index=True, tracking=3) + koli_lines = fields.One2many('sales.order.koli', 'sale_order_id', string='Sales Order Koli', auto_join=True) fulfillment_line_v2 = fields.One2many('sales.order.fulfillment.v2', 'sale_order_id', string='Fullfillment2') fullfillment_line = fields.One2many('sales.order.fullfillment', 'sales_order_id', string='Fullfillment') @@ -315,7 +326,17 @@ class SaleOrder(models.Model): "sale_order_id": self.id, }) self.shipping_option_id = shipping_option.id - + self.message_post( + body=( + f"<b>Estimasi pengiriman Indoteknik berhasil:</b><br/>" + f"Layanan: {shipping_option.name}<br/>" + f"ETD: {shipping_option.etd}<br/>" + f"Biaya: Rp {shipping_option.price:,}<br/>" + f"Provider: {shipping_option.provider}" + ), + message_type="comment", + ) + def action_estimate_shipping(self): if self.carrier_id.id in [1, 151]: self.action_indoteknik_estimate_shipping() @@ -354,6 +375,8 @@ class SaleOrder(models.Model): shipping_options.append((service, description, etd, value, courier['code'])) self.env["shipping.option"].search([('sale_order_id', '=', self.id)]).unlink() + + _logger.info(f"Shipping options: {shipping_options}") for service, description, etd, value, provider in shipping_options: self.env["shipping.option"].create({ @@ -363,12 +386,23 @@ class SaleOrder(models.Model): "etd": etd, "sale_order_id": self.id, }) + self.shipping_option_id = self.env["shipping.option"].search([('sale_order_id', '=', self.id)], limit=1).id - self.message_post(body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}<br/>Detail Lain:<br/>{'<br/>'.join([f'Service: {s[0]}, Description: {s[1]}, ETD: {s[2]} hari, Cost: Rp {s[3]}' for s in shipping_options])}") + _logger.info(f"Shipping option SO ID: {self.shipping_option_id}") + + self.message_post( + body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}<br/>Detail Lain:<br/>" + f"{'<br/>'.join([f'Service: {s[0]}, Description: {s[1]}, ETD: {s[2]} hari, Cost: Rp {s[3]}' for s in shipping_options])}", + message_type="comment" + ) + + # self.message_post(body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}<br/>Detail Lain:<br/>{'<br/>'.join([f'Service: {s[0]}, Description: {s[1]}, ETD: {s[2]} hari, Cost: Rp {s[3]}' for s in shipping_options])}", message_type="comment") + else: raise UserError("Gagal mendapatkan estimasi ongkir.") + def _call_rajaongkir_api(self, total_weight, destination_subsdistrict_id): url = 'https://pro.rajaongkir.com/api/cost' @@ -472,7 +506,7 @@ class SaleOrder(models.Model): def _compute_date_kirim(self): for rec in self: - picking = self.env['stock.picking'].search([('sale_id', '=', rec.id), ('state', 'not in', ['cancel'])], order='date_doc_kirim desc', limit=1) + picking = self.env['stock.picking'].search([('sale_id', '=', rec.id), ('state', 'not in', ['cancel']), ('name', 'not ilike', 'BU/PICK/%')], order='date_doc_kirim desc', limit=1) rec.date_kirim_ril = picking.date_doc_kirim rec.date_status_done = picking.date_done rec.date_driver_arrival = picking.driver_arrival_date @@ -683,12 +717,30 @@ class SaleOrder(models.Model): # @api.constrains('delivery_amt', 'carrier_id', 'shipping_cost_covered') def _validate_delivery_amt(self): - if self.delivery_amt < 1: - if(self.carrier_id.id == 1 or self.shipping_cost_covered == 'indoteknik') and not self.env.context.get('active_id', []): - if(self.carrier_id.id == 1): - raise UserError('Untuk Kurir Indoteknik Delivery, Estimasi Ongkos Kirim Harus di isi') + is_indoteknik = self.carrier_id.id == 1 or self.shipping_cost_covered == 'indoteknik' + is_active_id = not self.env.context.get('active_id', []) + + if is_indoteknik and is_active_id: + if self.delivery_amt == 0: + if self.carrier_id.id == 1: + raise UserError('Untuk Kurir Indoteknik Delivery, estimasi ongkos kirim belum diisi.') + else: + raise UserError('Untuk Shipping Covered Indoteknik, estimasi ongkos kirim belum diisi.') + + if self.delivery_amt < 100: + if self.carrier_id.id == 1: + raise UserError('Untuk Kurir Indoteknik Delivery, estimasi ongkos kirim belum memenuhi tarif minimum.') else: - raise UserError('Untuk Shipping Covered Indoteknik, Estimasi Ongkos Kirim Harus di isi') + raise UserError('Untuk Shipping Covered Indoteknik, estimasi ongkos kirim belum memenuhi tarif minimum.') + + + # if self.delivery_amt < 5000: + # if (self.carrier_id.id == 1 or self.shipping_cost_covered == 'indoteknik') and not self.env.context.get('active_id', []): + # if self.carrier_id.id == 1: + # raise UserError('Untuk Kurir Indoteknik Delivery, estimasi ongkos kirim belum memenuhi jumlah minimum.') + # else: + # raise UserError('Untuk Shipping Covered Indoteknik, estimasi ongkos kirim belum memenuhi jumlah minimum.') + def override_allow_create_invoice(self): if not self.env.user.is_accounting: @@ -1029,7 +1081,16 @@ class SaleOrder(models.Model): raise UserError("Product BOM belum dikonfirmasi di Manufacturing Orders. Silakan hubungi MD.") else: raise UserError("Product BOM tidak di temukan di manufacturing orders, silahkan hubungi MD") + + def check_duplicate_product(self): + for order in self: + for line in order.order_line: + search_product = self.env['sale.order.line'].search([('product_id', '=', line.product_id.id), ('order_id', '=', order.id)]) + if len(search_product) > 1: + raise UserError("Terdapat DUPLIKASI data pada Product {}".format(line.product_id.display_name)) + def sale_order_approve(self): + self.check_duplicate_product() self.check_product_bom() self.check_credit_limit() self.check_limit_so_to_invoice() @@ -1270,6 +1331,7 @@ class SaleOrder(models.Model): def action_confirm(self): for order in self: + order.check_duplicate_product() order.check_product_bom() order.check_credit_limit() order.check_limit_so_to_invoice() @@ -1442,6 +1504,9 @@ class SaleOrder(models.Model): def _compute_total_margin(self): for order in self: total_margin = sum(line.item_margin for line in order.order_line if line.product_id) + if order.ongkir_ke_xpdc: + total_margin -= order.ongkir_ke_xpdc + order.total_margin = total_margin def _compute_total_percent_margin(self): diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index aa616e62..36129f00 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -26,11 +26,11 @@ _biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1l class StockPicking(models.Model): _inherit = 'stock.picking' _order = 'final_seq ASC' - konfirm_koli_lines = fields.One2many('konfirm.koli', 'picking_id', string='Konfirm Koli', auto_join=True) - scan_koli_lines = fields.One2many('scan.koli', 'picking_id', string='Scan Koli', auto_join=True) - check_koli_lines = fields.One2many('check.koli', 'picking_id', string='Check Koli', auto_join=True) + konfirm_koli_lines = fields.One2many('konfirm.koli', 'picking_id', string='Konfirm Koli', auto_join=True, copy=False) + scan_koli_lines = fields.One2many('scan.koli', 'picking_id', string='Scan Koli', auto_join=True, copy=False) + check_koli_lines = fields.One2many('check.koli', 'picking_id', string='Check Koli', auto_join=True, copy=False) - check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True) + check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True, copy=False) barcode_product_lines = fields.One2many('barcode.product', 'picking_id', string='Barcode Product', auto_join=True) is_internal_use = fields.Boolean('Internal Use', help='flag which is internal use or not') account_id = fields.Many2one('account.account', string='Account') @@ -100,12 +100,11 @@ class StockPicking(models.Model): ('pengajuan1', 'Approval Finance'), ('approved', 'Approved'), ], string='Approval Return Status', readonly=True, copy=False, index=True, tracking=3, help="Approval Status untuk Return") - date_doc_kirim = fields.Datetime(string='Tanggal Kirim di SJ', help="Tanggal Kirim di cetakan SJ, tidak berpengaruh ke Accounting", tracking=True) + date_doc_kirim = fields.Datetime(string='Tanggal Kirim di SJ', help="Tanggal Kirim di cetakan SJ, tidak berpengaruh ke Accounting", tracking=True, copy=False) note_logistic = fields.Selection([ - ('hold', 'Hold by Sales'), + ('wait_so_together', 'Tunggu SO Barengan'), ('not_paid', 'Customer belum bayar'), - ('partial', 'Kirim Parsial'), - ('indent', 'Indent'), + ('reserve_stock', 'Reserve Stock'), ('waiting_schedule', 'Menunggu Jadwal Kirim'), ('self_pickup', 'Barang belum di pickup Customer'), ('expedition_closed', 'Eskpedisi belum buka') @@ -141,7 +140,8 @@ class StockPicking(models.Model): ('done', 'Done'), ('cancel', 'Cancelled'), ], string='Status Reserve', tracking=True, copy=False, help="The current state of the stock picking.") - notee = fields.Text(string="Note") + notee = fields.Text(string="Note SJ", help="Catatan untuk kirim barang") + note_info = fields.Text(string="Note Logistix (Text)", help="Catatan untuk pengiriman") state_approve_md = fields.Selection([ ('waiting', 'Waiting For Approve by MD'), ('pending', 'Pending (perlu koordinasi dengan MD)'), @@ -154,8 +154,33 @@ class StockPicking(models.Model): # record.show_state_approve_md = record.location_id.id == 47 or record.location_id.complete_name == "Virtual Locations/Gudang Selisih" quantity_koli = fields.Float(string="Quantity Koli", copy=False) total_mapping_koli = fields.Float(string="Total Mapping Koli", compute='_compute_total_mapping_koli') - so_lama = fields.Boolean('SO LAMA') - + so_lama = fields.Boolean('SO LAMA', copy=False) + linked_manual_bu_out = fields.Many2one('stock.picking', string='BU Out', copy=False) + + # def write(self, vals): + # if 'linked_manual_bu_out' in vals: + # for record in self: + # if (record.picking_type_code == 'internal' + # and 'BU/PICK/' in record.name): + # # Jika menghapus referensi (nilai di-set False/None) + # if record.linked_manual_bu_out and not vals['linked_manual_bu_out']: + # record.linked_manual_bu_out.state_packing = 'not_packing' + # # Jika menambahkan referensi baru + # elif vals['linked_manual_bu_out']: + # new_picking = self.env['stock.picking'].browse(vals['linked_manual_bu_out']) + # new_picking.state_packing = 'packing_done' + # return super().write(vals) + + # @api.model + # def create(self, vals): + # record = super().create(vals) + # if (record.picking_type_code == 'internal' + # and 'BU/PICK/' in record.name + # and vals.get('linked_manual_bu_out')): + # picking = self.env['stock.picking'].browse(vals['linked_manual_bu_out']) + # picking.state_packing = 'packing_done' + # return record + @api.depends('konfirm_koli_lines', 'konfirm_koli_lines.pick_id', 'konfirm_koli_lines.pick_id.quantity_koli') def _compute_total_mapping_koli(self): for record in self: @@ -220,21 +245,68 @@ class StockPicking(models.Model): final_seq = fields.Float(string='Remaining Time') shipping_method_so_id = fields.Many2one('delivery.carrier', string='Shipping Method SO', related='sale_id.carrier_id') state_packing = fields.Selection([('not_packing', 'Belum Packing'), ('packing_done', 'Sudah Packing')], string='Packing Status') - - @api.constrains('konfirm_koli_lines') - def _constrains_konfirm_koli_lines(self): - now = datetime.datetime.utcnow() - for picking in self: - if len(picking.konfirm_koli_lines) > 0: - picking.state_packing = 'packing_done' - else: - picking.state_packing = 'not_packing' + approval_invoice_date_id = fields.Many2one('approval.invoice.date', string='Approval Invoice Date') + last_update_date_doc_kirim = fields.Datetime(string='Last Update Tanggal Kirim') + + def _check_date_doc_kirim_modification(self): + for record in self: + if record.last_update_date_doc_kirim and not self.env.context.get('from_button_approve'): + kirim_date = fields.Datetime.from_string(record.last_update_date_doc_kirim) + now = fields.Datetime.now() + + deadline = kirim_date + timedelta(days=1) + deadline = deadline.replace(hour=10, minute=0, second=0) + + if now > deadline: + raise ValidationError( + _("Anda tidak dapat mengubah Tanggal Kirim setelah jam 10:00 pada hari berikutnya!") + ) + + @api.constrains('date_doc_kirim') + def _constrains_date_doc_kirim(self): + for rec in self: + rec.calculate_line_no() + + if rec.picking_type_code == 'outgoing' and 'BU/OUT/' in rec.name and rec.partner_id.id != 96868: + invoice = self.env['account.move'].search([('sale_id', '=', rec.sale_id.id), ('move_type', '=', 'out_invoice'), ('state', '=', 'posted')], limit=1, order='create_date desc') + + if invoice and not self.env.context.get('active_model') == 'stock.picking': + rec._check_date_doc_kirim_modification() + if rec.date_doc_kirim != invoice.invoice_date and not self.env.context.get('from_button_approve'): + get_approval_invoice_date = self.env['approval.invoice.date'].search([('picking_id', '=', rec.id),('state', '=', 'draft')], limit=1) + + if get_approval_invoice_date and get_approval_invoice_date.state == 'draft': + get_approval_invoice_date.date_doc_do = rec.date_doc_kirim + else: + approval_invoice_date = self.env['approval.invoice.date'].create({ + 'picking_id': rec.id, + 'date_invoice': invoice.invoice_date, + 'date_doc_do': rec.date_doc_kirim, + 'sale_id': rec.sale_id.id, + 'move_id': invoice.id, + 'partner_id': rec.partner_id.id + }) + + rec.approval_invoice_date_id = approval_invoice_date.id + + if approval_invoice_date: + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { 'title': 'Notification', 'message': 'Invoice Date Tidak Sesuai, Document Approval Invoice Date Terbuat', 'next': {'type': 'ir.actions.act_window_close'} }, + } + + rec.last_update_date_doc_kirim = datetime.datetime.utcnow() + @api.constrains('scan_koli_lines') def _constrains_scan_koli_lines(self): now = datetime.datetime.utcnow() for picking in self: if len(picking.scan_koli_lines) > 0: + if len(picking.scan_koli_lines) != picking.total_mapping_koli: + raise UserError("Scan Koli Tidak Sesuai Dengan Total Mapping Koli") + picking.driver_departure_date = now @api.depends('total_so_koli') @@ -930,6 +1002,8 @@ class StockPicking(models.Model): raise UserError('Hanya MD yang bisa Approve') def button_validate(self): + self.check_invoice_date() + threshold_datetime = waktu(2025, 4, 11, 6, 26) 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') @@ -941,7 +1015,6 @@ class StockPicking(models.Model): if self.location_id.id == 47 and self.env.user.id in users_in_group.mapped('id'): self.state_approve_md = 'done' - threshold_datetime = waktu(2025, 4, 11, 6, 26) if (len(self.konfirm_koli_lines) == 0 and 'BU/OUT/' in self.name @@ -963,6 +1036,9 @@ class StockPicking(models.Model): if len(self.check_koli_lines) == 0 and 'BU/PICK/' in self.name: raise UserError(_("Tidak ada koli! Harap periksa kembali.")) + 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: raise UserError(_("Tidak ada Check Product! Harap periksa kembali.")) @@ -1021,9 +1097,7 @@ class StockPicking(models.Model): if self.picking_type_code == 'outgoing' and 'BU/OUT/' in self.name: self.check_koli() res = super(StockPicking, self).button_validate() - self.calculate_line_no() self.date_done = datetime.datetime.utcnow() - self.driver_departure_date = datetime.datetime.utcnow() self.state_reserve = 'done' self.final_seq = 0 self.set_picking_code_out() @@ -1056,6 +1130,28 @@ class StockPicking(models.Model): self.send_mail_bills() return res + def check_invoice_date(self): + for picking in self: + if picking.picking_type_code != 'outgoing' or 'BU/OUT/' not in picking.name or picking.partner_id.id == 96868: + continue + + invoice = self.env['account.move'].search([('sale_id', '=', picking.sale_id.id)], limit=1) + + if not invoice: + continue + + if not picking.date_doc_kirim or not invoice.invoice_date: + raise UserError("Tanggal Kirim atau Tanggal Invoice belum diisi!") + + picking_date = fields.Date.to_date(picking.date_doc_kirim) + invoice_date = fields.Date.to_date(invoice.invoice_date) + + if picking_date != invoice_date: + raise UserError("Tanggal Kirim (%s) tidak sesuai dengan Tanggal Invoice (%s)!" % ( + picking_date.strftime('%d-%m-%Y'), + invoice_date.strftime('%d-%m-%Y') + )) + def set_picking_code_out(self): for picking in self: # Check if picking meets criteria @@ -1214,6 +1310,17 @@ class StockPicking(models.Model): line.sale_line_id = sale_line.id def write(self, vals): + if 'linked_manual_bu_out' in vals: + for record in self: + if (record.picking_type_code == 'internal' + and 'BU/PICK/' in record.name): + # Jika menghapus referensi (nilai di-set False/None) + if record.linked_manual_bu_out and not vals['linked_manual_bu_out']: + record.linked_manual_bu_out.state_packing = 'not_packing' + # Jika menambahkan referensi baru + elif vals['linked_manual_bu_out']: + new_picking = self.env['stock.picking'].browse(vals['linked_manual_bu_out']) + new_picking.state_packing = 'packing_done' self._use_faktur(vals) self.sync_sale_line(vals) for picking in self: @@ -1484,9 +1591,73 @@ class CheckProduct(models.Model): index=True, copy=False, ) - product_id = fields.Many2one('product.product', string='Product', required=True) - quantity = fields.Float(string='Quantity', default=1.0, required=True) + product_id = fields.Many2one('product.product', string='Product') + quantity = fields.Float(string='Quantity') status = fields.Char(string='Status', compute='_compute_status') + code_product = fields.Char(string='Code Product') + + @api.onchange('code_product') + def _onchange_code_product(self): + if not self.code_product: + return + + # Cari product berdasarkan default_code, barcode, atau barcode_box + product = self.env['product.product'].search([ + '|', + ('default_code', '=', self.code_product), + '|', + ('barcode', '=', self.code_product), + ('barcode_box', '=', self.code_product) + ], limit=1) + + if not product: + raise UserError("Product tidak ditemukan") + + # Jika scan barcode_box, set quantity sesuai qty_pcs_box + if product.barcode_box == self.code_product: + self.product_id = product.id + self.quantity = product.qty_pcs_box + self.code_product = product.default_code or product.barcode + # return { + # 'warning': { + # 'title': 'Info',8994175025871 + + # 'message': f'Product box terdeteksi. Quantity di-set ke {product.qty_pcs_box}' + # } + # } + else: + # Jika scan biasa + self.product_id = product.id + self.code_product = product.default_code or product.barcode + self.quantity = 1 + + def unlink(self): + # Get all affected pickings before deletion + pickings = self.mapped('picking_id') + + # Store product_ids that will be deleted + deleted_product_ids = self.mapped('product_id') + + # Perform the deletion + result = super(CheckProduct, self).unlink() + + # After deletion, update moves for affected pickings + for picking in pickings: + # For products that were completely removed (no remaining check.product lines) + remaining_product_ids = picking.check_product_lines.mapped('product_id') + removed_product_ids = deleted_product_ids - remaining_product_ids + + # Set quantity_done to 0 for moves of completely removed products + moves_to_reset = picking.move_ids_without_package.filtered( + lambda move: move.product_id in removed_product_ids + ) + for move in moves_to_reset: + move.quantity_done = 0.0 + + # Also sync remaining products in case their totals changed + self._sync_check_product_to_moves(picking) + + return result @api.depends('quantity') def _compute_status(self): @@ -1586,7 +1757,7 @@ class CheckProduct(models.Model): # Find existing lines for the same product, excluding the current line existing_lines = record.picking_id.check_product_lines.filtered( - lambda line: line.product_id == record.product_id and line.id != record.id + lambda line: line.product_id == record.product_id ) if existing_lines: @@ -1594,7 +1765,7 @@ class CheckProduct(models.Model): first_line = existing_lines[0] # Calculate the total quantity after addition - total_quantity = sum(existing_lines.mapped('quantity')) - record.quantity + total_quantity = sum(existing_lines.mapped('quantity')) if total_quantity > total_qty_in_moves: raise UserError(( @@ -1602,7 +1773,7 @@ class CheckProduct(models.Model): ) % (record.product_id.display_name)) else: # Check if the quantity exceeds the allowed total - if record.quantity > total_qty_in_moves: + if record.quantity == total_qty_in_moves: raise UserError(( "Quantity Product '%s' sudah melebihi quantity demand." ) % (record.product_id.display_name)) @@ -1626,9 +1797,21 @@ class BarcodeProduct(models.Model): product_id = fields.Many2one('product.product', string='Product', required=True) barcode = fields.Char(string='Barcode') + def check_duplicate_barcode(self): + barcode_product = self.env['product.product'].search([('barcode', '=', self.barcode)]) + + if barcode_product: + raise UserError('Barcode sudah digunakan {}'.format(barcode_product.display_name)) + + barcode_box = self.env['product.product'].search([('barcode_box', '=', self.barcode)]) + + if barcode_box: + raise UserError('Barcode box sudah digunakan {}'.format(barcode_box.display_name)) + @api.constrains('barcode') def send_barcode_to_product(self): for record in self: + record.check_duplicate_barcode() if record.barcode and not record.product_id.barcode: record.product_id.barcode = record.barcode else: @@ -1683,15 +1866,25 @@ class ScanKoli(models.Model): string="Progress Scan Koli", compute="_compute_scan_koli_progress" ) + code_koli = fields.Char(string='Code Koli') - def _compute_scan_koli_progress(self): - for scan in self: - if scan.picking_id: - all_scans = self.env['scan.koli'].search([('picking_id', '=', scan.picking_id.id)], order='id') - if all_scans: - scan_index = list(all_scans).index(scan) + 1 # Nomor urut scan - total_so_koli = scan.picking_id.total_so_koli - scan.scan_koli_progress = f"{scan_index}/{total_so_koli}" if total_so_koli else "0/0" + @api.onchange('code_koli') + def _onchange_code_koli(self): + if self.code_koli: + koli = self.env['sales.order.koli'].search([('koli_id.koli', '=', self.code_koli)], limit=1) + if koli: + self.write({'koli_id': koli.id}) + else: + raise UserError('Koli tidak ditemukan') + + # def _compute_scan_koli_progress(self): + # for scan in self: + # if scan.picking_id: + # all_scans = self.env['scan.koli'].search([('picking_id', '=', scan.picking_id.id)], order='id') + # if all_scans: + # scan_index = list(all_scans).index(scan) + 1 # Nomor urut scan + # total_so_koli = scan.picking_id.total_so_koli + # scan.scan_koli_progress = f"{scan_index}/{total_so_koli}" if total_so_koli else "0/0" @api.onchange('koli_id') def _onchange_koli_compare_with_konfirm_koli(self): @@ -1763,13 +1956,21 @@ class ScanKoli(models.Model): def _compute_scan_koli_progress(self): for scan in self: - if scan.picking_id: + if not scan.picking_id: + scan.scan_koli_progress = "0/0" + continue + + try: all_scans = self.env['scan.koli'].search([('picking_id', '=', scan.picking_id.id)], order='id') if all_scans: - scan_index = list(all_scans).index(scan) + 1 # Nomor urut scan - total_so_koli = scan.picking_id.total_so_koli - scan.scan_koli_progress = f"{scan_index}/{total_so_koli}" if total_so_koli else "0/0" - + scan_index = list(all_scans).index(scan) + 1 + total_so_koli = scan.picking_id.total_so_koli or 0 + scan.scan_koli_progress = f"{scan_index}/{total_so_koli}" + else: + scan.scan_koli_progress = "0/0" + except Exception: + # Fallback in case of any error + scan.scan_koli_progress = "0/0" @api.constrains('picking_id', 'picking_id.total_so_koli') def _check_koli_validation(self): for scan in self.picking_id.scan_koli_lines: diff --git a/indoteknik_custom/models/voucher.py b/indoteknik_custom/models/voucher.py index 101d4bcf..7b458d01 100644 --- a/indoteknik_custom/models/voucher.py +++ b/indoteknik_custom/models/voucher.py @@ -265,3 +265,19 @@ class Voucher(models.Model): tnc.append(f'<li>{line_tnc}</li>') return ' '.join(tnc) + # copy semua data kalau diduplicate + def copy(self, default=None): + default = dict(default or {}) + voucher_lines = [] + + for line in self.voucher_line: + voucher_lines.append((0, 0, { + 'manufacture_id': line.manufacture_id.id, + 'discount_amount': line.discount_amount, + 'discount_type': line.discount_type, + 'min_purchase_amount': line.min_purchase_amount, + 'max_discount_amount': line.max_discount_amount, + })) + + default['voucher_line'] = voucher_lines + return super(Voucher, self).copy(default)
\ No newline at end of file diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 4d164bcb..46ab4c1f 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -138,6 +138,7 @@ access_shipment_group,access.shipment.group,model_shipment_group,,1,1,1,1 access_shipment_group_line,access.shipment.group.line,model_shipment_group_line,,1,1,1,1 access_sales_order_reject,access.sales.order.reject,model_sales_order_reject,,1,1,1,1 access_approval_date_doc,access.approval.date.doc,model_approval_date_doc,,1,1,1,1 +access_approval_invoice_date,access.approval.invoice.date,model_approval_invoice_date,,1,1,1,1 access_account_tax,access.account.tax,model_account_tax,,1,1,1,1 access_approval_unreserve,access.approval.unreserve,model_approval_unreserve,,1,1,1,1 access_approval_unreserve_line,access.approval.unreserve.line,model_approval_unreserve_line,,1,1,1,1 diff --git a/indoteknik_custom/views/approval_invoice_date.xml b/indoteknik_custom/views/approval_invoice_date.xml new file mode 100644 index 00000000..31f346e7 --- /dev/null +++ b/indoteknik_custom/views/approval_invoice_date.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<odoo> + <record id="approval_invoice_date_tree" model="ir.ui.view"> + <field name="name">approval.invoice.date.tree</field> + <field name="model">approval.invoice.date</field> + <field name="arch" type="xml"> + <tree> + <field name="number"/> + <field name="picking_id"/> + <field name="partner_id"/> + <field name="sale_id"/> + <field name="date_doc_do"/> + <field name="date_invoice"/> + <field name="state" widget="badge" decoration-danger="state == 'cancel'" + decoration-success="state == 'done'" + decoration-info="state == 'draft'"/> + <field name="approve_date"/> + <field name="approve_by"/> + <field name="create_uid"/> + </tree> + </field> + </record> + + <record id="approval_invoice_date_form" model="ir.ui.view"> + <field name="name">approval.invoice.date.form</field> + <field name="model">approval.invoice.date</field> + <field name="arch" type="xml"> + <form> + <header> + <button name="button_approve" + string="Approve" + type="object" + attrs="{'invisible': [('state', '=', 'done')]}" + /> + <button name="button_cancel" + string="Cancel" + type="object" + attrs="{'invisible': [('state', '=', 'cancel')]}" + /> + <field name="state" widget="statusbar" + statusbar_visible="draft,cancel,done" + statusbar_colors='{"cancel":"red", "done":"green"}'/> + </header> + <sheet string="Approval Invoice Date"> + <group> + <group> + <field name="number"/> + <field name="picking_id"/> + <field name="partner_id"/> + <field name="sale_id"/> + <field name="move_id"/> + <field name="date_doc_do"/> + <field name="date_invoice"/> + <field name="approve_date"/> + <field name="approve_by"/> + <field name="create_uid"/> + <field name="note" attrs="{'invisible': [('state', '!=', 'cancel')]}"/> + </group> + </group> + </sheet> + </form> + </field> + </record> + + <record id="view_approval_invoice_date_filter" model="ir.ui.view"> + <field name="name">approval.invoice.date.list.select</field> + <field name="model">approval.invoice.date</field> + <field name="priority" eval="15"/> + <field name="arch" type="xml"> + <search string="Search Approval Invoice Date"> + <field name="number"/> + <field name="partner_id"/> + <field name="picking_id"/> + <field name="sale_id"/> + </search> + </field> + </record> + + <record id="approval_invoice_date_action" model="ir.actions.act_window"> + <field name="name">Approval Invoice Date</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">approval.invoice.date</field> + <field name="view_mode">tree,form</field> + </record> + + <menuitem id="menu_approval_invoice_date" name="Approval Invoice Date" + parent="account.menu_finance_receivables" + action="approval_invoice_date_action" + sequence="100" + /> + +</odoo>
\ No newline at end of file diff --git a/indoteknik_custom/views/barcoding_product.xml b/indoteknik_custom/views/barcoding_product.xml index c7473d39..92064ee5 100644 --- a/indoteknik_custom/views/barcoding_product.xml +++ b/indoteknik_custom/views/barcoding_product.xml @@ -34,12 +34,13 @@ <group> <field name="product_id" required="1"/> <field name="type" required="1"/> - <field name="quantity" attrs="{'invisible': [['type', 'in', ('barcoding')]], 'required': [['type', 'not in', ('barcoding')]]}"/> + <field name="quantity" attrs="{'invisible': [['type', 'in', ('barcoding','barcoding_box')]], 'required': [['type', 'not in', ('barcoding')]]}"/> <field name="barcode" attrs="{'invisible': [['type', 'in', ('print')]], 'required': [['type', 'not in', ('print')]]}"/> + <field name="qty_pcs_box" attrs="{'invisible': [['type', 'in', ('print','barcoding')]], 'required': [['type', 'not in', ('print')]]}"/> </group> </group> <notebook> - <page string="Line" attrs="{'invisible': [['type', 'in', ('barcoding')]]}"> + <page string="Line" attrs="{'invisible': [['type', 'in', ('barcoding','barcoding_box')]]}"> <field name="barcoding_product_line"/> </page> </notebook> diff --git a/indoteknik_custom/views/ir_sequence.xml b/indoteknik_custom/views/ir_sequence.xml index dfb56100..9f980751 100644 --- a/indoteknik_custom/views/ir_sequence.xml +++ b/indoteknik_custom/views/ir_sequence.xml @@ -21,6 +21,16 @@ <field name="number_increment">1</field> </record> + <record id="sequence_invoice_date" model="ir.sequence"> + <field name="name">Approval Invoice Date</field> + <field name="code">approval.invoice.date</field> + <field name="active">TRUE</field> + <field name="prefix">AID/%(year)s/</field> + <field name="padding">5</field> + <field name="number_next">1</field> + <field name="number_increment">1</field> + </record> + <record id="sequence_vendor_approval" model="ir.sequence"> <field name="name">Vendor Approval</field> <field name="code">vendor.approval</field> diff --git a/indoteknik_custom/views/product_template.xml b/indoteknik_custom/views/product_template.xml index af21984a..076a8082 100755 --- a/indoteknik_custom/views/product_template.xml +++ b/indoteknik_custom/views/product_template.xml @@ -62,6 +62,8 @@ <field name="inherit_id" ref="product.product_normal_form_view"/> <field name="arch" type="xml"> <field name="last_update_solr" position="after"> + <field name="barcode_box" /> + <field name="qty_pcs_box" /> <field name="clean_website_description" /> <field name="qr_code_variant" widget="image" readonly="True"/> </field> diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 2c64181e..79a095fb 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -1,180 +1,214 @@ -<?xml version="1.0" encoding="UTF-8" ?> +<?xml version="1.0" encoding="UTF-8"?> <odoo> <data> <record id="sale_order_form_view_inherit" model="ir.ui.view"> <field name="name">Sale Order</field> <field name="model">sale.order</field> - <field name="inherit_id" ref="sale.view_order_form"/> + <field name="inherit_id" ref="sale.view_order_form" /> <field name="arch" type="xml"> <button id="action_confirm" position="after"> <button name="calculate_line_no" - string="Create No" - type="object" + string="Create No" + type="object" /> <button name="sale_order_approve" - string="Ask Approval" - type="object" - attrs="{'invisible': [('approval_status', '=', ['approved'])]}" + string="Ask Approval" + type="object" + attrs="{'invisible': [('approval_status', '=', ['approved'])]}" /> <button name="action_web_approve" - string="Web Approve" - type="object" - attrs="{'invisible': ['|', '|', ('create_uid', '!=', 25), ('web_approval', '!=', False), ('state', '!=', 'draft')]}" + string="Web Approve" + type="object" + attrs="{'invisible': ['|', '|', ('create_uid', '!=', 25), ('web_approval', '!=', False), ('state', '!=', 'draft')]}" /> - <button name="indoteknik_custom.action_view_uangmuka_penjualan" string="UangMuka" - type="action" attrs="{'invisible': [('approval_status', '!=', 'approved')]}"/> + <button name="indoteknik_custom.action_view_uangmuka_penjualan" + string="UangMuka" + type="action" attrs="{'invisible': [('approval_status', '!=', 'approved')]}" /> </button> <field name="payment_term_id" position="after"> - <field name="create_uid" invisible="1"/> - <field name="create_date" invisible="1"/> - <field name="shipping_cost_covered" attrs="{'required': ['|', ('create_date', '>', '2023-06-15'), ('create_date', '=', False)]}"/> - <field name="shipping_paid_by" attrs="{'required': ['|', ('create_date', '>', '2023-06-15'), ('create_date', '=', False)]}"/> - <field name="delivery_amt"/> - <field name="fee_third_party"/> - <field name="biaya_lain_lain"/> - <field name="total_percent_margin"/> - <field name="total_margin_excl_third_party" readonly="1"/> - <field name="type_promotion"/> - <label for="voucher_id"/> + <field name="create_uid" invisible="1" /> + <field name="create_date" invisible="1" /> + <field name="shipping_cost_covered" + attrs="{'required': ['|', ('create_date', '>', '2023-06-15'), ('create_date', '=', False)]}" /> + <field name="shipping_paid_by" + attrs="{'required': ['|', ('create_date', '>', '2023-06-15'), ('create_date', '=', False)]}" /> + <field name="delivery_amt" /> + <field name="ongkir_ke_xpdc" /> + <field name="metode_kirim_ke_xpdc" /> + <field name="fee_third_party" /> + <field name="biaya_lain_lain" /> + <field name="total_percent_margin" /> + <field name="total_margin_excl_third_party" readonly="1" /> + <field name="type_promotion" /> + <label for="voucher_id" /> <div class="o_row"> - <field name="voucher_id" id="voucher_id" attrs="{'readonly': ['|', ('state', 'not in', ['draft', 'sent']), ('applied_voucher_id', '!=', False)]}"/> + <field name="voucher_id" id="voucher_id" + attrs="{'readonly': ['|', ('state', 'not in', ['draft', 'sent']), ('applied_voucher_id', '!=', False)]}" /> <field name="applied_voucher_id" invisible="1" /> - <button name="action_apply_voucher" type="object" string="Apply" confirm="Anda yakin untuk menggunakan voucher?" help="Apply the selected voucher" class="btn-link mb-1 px-0" icon="fa-plus" + <button name="action_apply_voucher" type="object" string="Apply" + confirm="Anda yakin untuk menggunakan voucher?" + help="Apply the selected voucher" class="btn-link mb-1 px-0" + icon="fa-plus" attrs="{'invisible': ['|', '|', ('voucher_id', '=', False), ('state', 'not in', ['draft', 'sent']), ('applied_voucher_id', '!=', False)]}" /> - <button name="cancel_voucher" type="object" string="Cancel" confirm="Anda yakin untuk membatalkan penggunaan voucher?" help="Cancel applied voucher" class="btn-link mb-1 px-0" icon="fa-times" + <button name="cancel_voucher" type="object" string="Cancel" + confirm="Anda yakin untuk membatalkan penggunaan voucher?" + help="Cancel applied voucher" class="btn-link mb-1 px-0" icon="fa-times" attrs="{'invisible': ['|', ('applied_voucher_id', '=', False), ('state', 'not in', ['draft','sent'])]}" /> </div> - <label for="voucher_shipping_id"/> + <label for="voucher_shipping_id" /> <div class="o_row"> - <field name="voucher_shipping_id" id="voucher_shipping_id" attrs="{'readonly': ['|', ('state', 'not in', ['draft', 'sent']), ('applied_voucher_shipping_id', '!=', False)]}"/> + <field name="voucher_shipping_id" id="voucher_shipping_id" + attrs="{'readonly': ['|', ('state', 'not in', ['draft', 'sent']), ('applied_voucher_shipping_id', '!=', False)]}" /> <field name="applied_voucher_shipping_id" invisible="1" /> - <button name="action_apply_voucher_shipping" type="object" string="Apply" confirm="Anda yakin untuk menggunakan voucher?" help="Apply the selected voucher" class="btn-link mb-1 px-0" icon="fa-plus" + <button name="action_apply_voucher_shipping" type="object" string="Apply" + confirm="Anda yakin untuk menggunakan voucher?" + help="Apply the selected voucher" class="btn-link mb-1 px-0" + icon="fa-plus" attrs="{'invisible': ['|', '|', ('voucher_id', '=', False), ('state', 'not in', ['draft', 'sent']), ('applied_voucher_shipping_id', '!=', False)]}" /> - <button name="cancel_voucher_shipping" type="object" string="Cancel" confirm="Anda yakin untuk membatalkan penggunaan voucher?" help="Cancel applied voucher" class="btn-link mb-1 px-0" icon="fa-times" + <button name="cancel_voucher_shipping" type="object" string="Cancel" + confirm="Anda yakin untuk membatalkan penggunaan voucher?" + help="Cancel applied voucher" class="btn-link mb-1 px-0" icon="fa-times" attrs="{'invisible': ['|', ('applied_voucher_shipping_id', '=', False), ('state', 'not in', ['draft','sent'])]}" /> </div> <button name="calculate_selling_price" - string="Calculate Selling Price" - type="object" + string="Calculate Selling Price" + type="object" /> </field> <field name="source_id" position="attributes"> <attribute name="invisible">1</attribute> </field> <field name="user_id" position="after"> - <field name="helper_by_id" readonly="1"/> - <field name="compute_fullfillment" invisible="1"/> + <field name="helper_by_id" readonly="1" /> + <field name="compute_fullfillment" invisible="1" /> </field> <field name="tag_ids" position="after"> - <field name="eta_date_start"/> - <t t-esc="' to '"/> - <field name="eta_date" readonly="1"/> + <field name="eta_date_start" /> + <t t-esc="' to '" /> + <field name="eta_date" readonly="1" /> <field name="expected_ready_to_ship" /> - <field name="flash_sale"/> - <field name="margin_after_delivery_purchase"/> - <field name="percent_margin_after_delivery_purchase"/> - <field name="total_weight"/> - <field name="pareto_status"/> + <field name="flash_sale" /> + <field name="margin_after_delivery_purchase" /> + <field name="percent_margin_after_delivery_purchase" /> + <field name="total_weight" /> + <field name="pareto_status" /> </field> <field name="analytic_account_id" position="after"> - <field name="customer_type" required="1"/> - <field name="npwp" placeholder='99.999.999.9-999.999' required="1"/> - <field name="sppkp" attrs="{'required': [('customer_type', '=', 'pkp')]}"/> - <field name="email" required="1"/> - <field name="unreserve_id"/> - <field name="due_id" readonly="1"/> - <field name="vendor_approval_id" readonly="1" widget="many2many_tags"/> - <field name="source_id" domain="[('id', 'in', [32, 59, 60, 61])]" required="1"/> + <field name="customer_type" required="1" /> + <field name="npwp" placeholder='99.999.999.9-999.999' required="1" /> + <field name="sppkp" attrs="{'required': [('customer_type', '=', 'pkp')]}" /> + <field name="email" required="1" /> + <field name="unreserve_id" /> + <field name="due_id" readonly="1" /> + <field name="vendor_approval_id" readonly="1" widget="many2many_tags" /> + <field name="source_id" domain="[('id', 'in', [32, 59, 60, 61])]" required="1" /> <button name="override_allow_create_invoice" - string="Override Create Invoice" - type="object" + string="Override Create Invoice" + type="object" /> - <button string="Estimate Shipping" type="object" name="action_estimate_shipping"/> + <button string="Estimate Shipping" type="object" name="action_estimate_shipping" /> </field> <field name="partner_shipping_id" position="after"> - <field name="real_shipping_id"/> - <field name="real_invoice_id"/> + <field name="real_shipping_id" /> + <field name="real_invoice_id" /> <field name="approval_status" /> - <field name="sales_tax_id" domain="[('type_tax_use','=','sale'), ('active', '=', True)]" required="1"/> - <field name="carrier_id" required="1"/> - <field name="delivery_service_type" readonly="1"/> - <field name="shipping_option_id"/> + <field name="sales_tax_id" + domain="[('type_tax_use','=','sale'), ('active', '=', True)]" required="1" /> + <field name="carrier_id" required="1" /> + <field name="delivery_service_type" readonly="1" /> + <field name="shipping_option_id" /> </field> <field name="medium_id" position="after"> - <field name="date_doc_kirim" readonly="1"/> - <field name="notification" readonly="1"/> + <field name="date_doc_kirim" readonly="1" /> + <field name="notification" readonly="1" /> </field> - <xpath expr="//form/sheet/notebook/page/field[@name='order_line']" position="attributes"> + <xpath expr="//form/sheet/notebook/page/field[@name='order_line']" + position="attributes"> <attribute name="attrs"> {'readonly': [('state', 'in', ('done','cancel'))]} </attribute> </xpath> - <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree" position="inside"> - <field name="desc_updatable" invisible="1"/> + <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree" + position="inside"> + <field name="desc_updatable" invisible="1" /> </xpath> - <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='name']" position="attributes"> + <xpath + expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='name']" + position="attributes"> <attribute name="modifiers"> {'readonly': [('desc_updatable', '=', False)]} </attribute> </xpath> - <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='price_unit']" position="attributes"> + <xpath + expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='price_unit']" + position="attributes"> <attribute name="attrs"> { - 'readonly': [ - '|', - ('qty_invoiced', '>', 0), - ('parent.approval_status', '!=', False) - ] + 'readonly': [ + '|', + ('qty_invoiced', '>', 0), + ('parent.approval_status', '!=', False) + ] } </attribute> </xpath> <div name="invoice_lines" position="before"> - <div name="vendor_id" groups="base.group_no_one" attrs="{'invisible': [('display_type', '!=', False)]}"> - <label for="vendor_id"/> + <div name="vendor_id" groups="base.group_no_one" + attrs="{'invisible': [('display_type', '!=', False)]}"> + <label for="vendor_id" /> <div name="vendor_id"> - <field name="vendor_id" - attrs="{'readonly': [('parent.approval_status', '=', 'approved')]}" - domain="[('parent_id', '=', False)]" - options="{'no_create': True}" class="oe_inline" /> + <field name="vendor_id" + attrs="{'readonly': [('parent.approval_status', '=', 'approved')]}" + domain="[('parent_id', '=', False)]" + options="{'no_create': True}" class="oe_inline" /> </div> </div> </div> - + <div name="invoice_lines" position="before"> - <div name="purchase_price" groups="base.group_no_one" attrs="{'invisible': [('display_type', '!=', False)]}"> - <label for="purchase_price"/> - <field name="purchase_price"/> + <div name="purchase_price" groups="base.group_no_one" + attrs="{'invisible': [('display_type', '!=', False)]}"> + <label for="purchase_price" /> + <field name="purchase_price" /> </div> </div> <div name="invoice_lines" position="before"> - <div name="purchase_tax_id" groups="base.group_no_one" attrs="{'invisible': [('display_type', '!=', False)]}"> - <label for="purchase_tax_id"/> + <div name="purchase_tax_id" groups="base.group_no_one" + attrs="{'invisible': [('display_type', '!=', False)]}"> + <label for="purchase_tax_id" /> <div name="purchase_tax_id"> - <field name="purchase_tax_id"/> + <field name="purchase_tax_id" /> </div> </div> </div> <div name="invoice_lines" position="before"> - <div name="item_percent_margin" groups="base.group_no_one" attrs="{'invisible': [('display_type', '!=', False)]}"> - <label for="item_percent_margin"/> - <field name="item_percent_margin"/> + <div name="item_percent_margin" groups="base.group_no_one" + attrs="{'invisible': [('display_type', '!=', False)]}"> + <label for="item_percent_margin" /> + <field name="item_percent_margin" /> </div> </div> <div name="invoice_lines" position="before"> - <div name="price_subtotal" groups="base.group_no_one" attrs="{'invisible': [('display_type', '!=', False)]}"> - <label for="price_subtotal"/> - <field name="price_subtotal"/> + <div name="price_subtotal" groups="base.group_no_one" + attrs="{'invisible': [('display_type', '!=', False)]}"> + <label for="price_subtotal" /> + <field name="price_subtotal" /> </div> </div> - <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='price_total']" position="after"> - <field name="qty_free_bu" optional="hide"/> - <field name="vendor_id" attrs="{'readonly': [('parent.approval_status', '=', 'approved')], 'invisible': [('display_type', '!=', False)]}" domain="[('parent_id', '=', False)]" options="{'no_create':True}"/> - <field name="vendor_md_id" optional="hide"/> - <field name="purchase_price" attrs=" + <xpath + expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='price_total']" + position="after"> + <field name="qty_free_bu" optional="hide" /> + <field name="vendor_id" + attrs="{'readonly': [('parent.approval_status', '=', 'approved')], 'invisible': [('display_type', '!=', False)]}" + domain="[('parent_id', '=', False)]" options="{'no_create':True}" /> + <field name="vendor_md_id" optional="hide" /> + <field name="purchase_price" + attrs=" { 'readonly': [ '|', @@ -182,56 +216,72 @@ ('parent.approval_status', '!=', False) ] } - "/> - <field name="purchase_price_md" optional="hide"/> - <field name="purchase_tax_id" attrs="{'readonly': [('parent.approval_status', '!=', False)]}" domain="[('type_tax_use','=','purchase')]" options="{'no_create':True}"/> - <field name="item_percent_margin"/> - <field name="item_margin" optional="hide"/> - <field name="margin_md" optional="hide"/> - <field name="note" optional="hide"/> - <field name="note_procurement" optional="hide"/> - <field name="vendor_subtotal" optional="hide"/> - <field name="weight" optional="hide"/> - <field name="amount_voucher_disc" string="Voucher" readonly="1" optional="hide"/> - <field name="order_promotion_id" string="Promotion" readonly="1" optional="hide"/> + " /> + <field name="purchase_price_md" optional="hide" /> + <field name="purchase_tax_id" + attrs="{'readonly': [('parent.approval_status', '!=', False)]}" + domain="[('type_tax_use','=','purchase')]" options="{'no_create':True}" /> + <field name="item_percent_margin" /> + <field name="item_margin" optional="hide" /> + <field name="margin_md" optional="hide" /> + <field name="note" optional="hide" /> + <field name="note_procurement" optional="hide" /> + <field name="vendor_subtotal" optional="hide" /> + <field name="weight" optional="hide" /> + <field name="amount_voucher_disc" string="Voucher" readonly="1" optional="hide" /> + <field name="order_promotion_id" string="Promotion" readonly="1" optional="hide" /> </xpath> - <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='product_id']" position="before"> - <field name="line_no" readonly="1" optional="hide"/> + <xpath + expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='product_id']" + position="before"> + <field name="line_no" readonly="1" optional="hide" /> </xpath> - <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='qty_delivered']" position="before"> - <field name="qty_reserved" invisible="1"/> - <field name="reserved_from" readonly="1" optional="hide"/> + <xpath + expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='qty_delivered']" + position="before"> + <field name="qty_reserved" invisible="1" /> + <field name="reserved_from" readonly="1" optional="hide" /> </xpath> - <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='product_id']" position="attributes"> + <xpath + expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='product_id']" + position="attributes"> <attribute name="options">{'no_create': True}</attribute> </xpath> - <!-- <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='tax_id']" position="attributes"> + <!-- <xpath + expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='tax_id']" + position="attributes"> <attribute name="required">1</attribute> </xpath> --> <field name="amount_total" position="after"> - <field name="grand_total"/> + <field name="grand_total" /> <label for="amount_voucher_disc" string="Voucher" /> <div> - <field class="mb-0" name="amount_voucher_disc" string="Voucher" readonly="1"/> - <div class="text-right mb-2"><small>*Hanya informasi</small></div> + <field class="mb-0" name="amount_voucher_disc" string="Voucher" readonly="1" /> + <div class="text-right mb-2"> + <small>*Hanya informasi</small> + </div> </div> <label for="amount_voucher_shipping_disc" string="Voucher Shipping" /> <div> - <field class="mb-0" name="amount_voucher_shipping_disc" string="Voucher Shipping" readonly="1"/> - <div class="text-right mb-2"><small>*Hanya informasi</small></div> + <field class="mb-0" name="amount_voucher_shipping_disc" + string="Voucher Shipping" readonly="1" /> + <div class="text-right mb-2"> + <small>*Hanya informasi</small> + </div> </div> - <field name="total_margin"/> - <field name="total_percent_margin"/> + <field name="total_margin" /> + <field name="total_percent_margin" /> </field> <field name="effective_date" position="after"> - <field name="carrier_id"/> - <field name="estimated_arrival_days"/> - <field name="picking_iu_id"/> - <field name="note_ekspedisi"/> + <field name="carrier_id" /> + <field name="estimated_arrival_days" /> + <field name="picking_iu_id" /> + <field name="note_ekspedisi" /> </field> <field name="carrier_id" position="attributes"> <attribute name="attrs"> - {'readonly': [('approval_status', '=', 'approved'), ('state', 'not in', ['cancel','draft'])]} + {'readonly': [('approval_status', '=', 'approved'), ('state', 'not in', + ['cancel','draft'])]} </attribute> </field> @@ -239,22 +289,22 @@ <page string="Website" name="customer_purchase_order"> <group> <group> - <field name="partner_purchase_order_name" readonly="True"/> - <field name="partner_purchase_order_description" readonly="True"/> - <field name="partner_purchase_order_file" readonly="True"/> - <field name="note_website" readonly="True"/> - <field name="web_approval" readonly="True"/> + <field name="partner_purchase_order_name" readonly="True" /> + <field name="partner_purchase_order_description" readonly="True" /> + <field name="partner_purchase_order_file" readonly="True" /> + <field name="note_website" readonly="True" /> + <field name="web_approval" readonly="True" /> </group> <group> <button name="generate_payment_link_midtrans_sales_order" - string="Create Payment Link" - type="object" + string="Create Payment Link" + type="object" /> - <field name="payment_link_midtrans" readonly="True" widget="url"/> - <field name="gross_amount" readonly="True"/> - <field name="payment_type" readonly="True"/> - <field name="payment_status" readonly="True"/> - <field name="payment_qr_code" widget="image" readonly="True"/> + <field name="payment_link_midtrans" readonly="True" widget="url" /> + <field name="gross_amount" readonly="True" /> + <field name="payment_type" readonly="True" /> + <field name="payment_status" readonly="True" /> + <field name="payment_qr_code" widget="image" readonly="True" /> </group> </group> </page> @@ -275,89 +325,91 @@ </field> </page> <page string="Matches PO" name="page_matches_po" invisible="1"> - <field name="order_sales_match_line" readonly="1"/> + <field name="order_sales_match_line" readonly="1" /> </page> <!-- <page string="Fullfillment" name="page_sale_order_fullfillment"> <field name="fullfillment_line" readonly="1"/> </page> --> <page string="Fulfillment v2" name="page_sale_order_fullfillment2"> - <field name="fulfillment_line_v2" readonly="1"/> + <field name="fulfillment_line_v2" readonly="1" /> </page> <page string="Reject Line" name="page_sale_order_reject_line"> - <field name="reject_line" readonly="1"/> + <field name="reject_line" readonly="1" /> </page> <page string="Koli" name="page_sales_order_koli_line"> - <field name="koli_lines" readonly="1"/> + <field name="koli_lines" readonly="1" /> </page> </page> </field> </record> - <!-- Wizard for Reject Reason --> - <record id="view_cancel_reason_order_form" model="ir.ui.view"> - <field name="name">cancel.reason.order.form</field> - <field name="model">cancel.reason.order</field> - <field name="arch" type="xml"> - <form string="Cancel Reason"> - <group> - <field name="reason_cancel" widget="selection"/> - <field name="attachment_bukti" widget="many2many_binary" required="1"/> - <field name="nomor_so_pengganti" attrs="{'invisible': [('reason_cancel', '!=', 'ganti_quotation')]}"/> - </group> - <footer> - <button string="Confirm" type="object" name="confirm_reject" class="btn-primary"/> - <button string="Cancel" class="btn-secondary" special="cancel"/> - </footer> - </form> - </field> - </record> + <!-- Wizard for Reject Reason --> + <record id="view_cancel_reason_order_form" model="ir.ui.view"> + <field name="name">cancel.reason.order.form</field> + <field name="model">cancel.reason.order</field> + <field name="arch" type="xml"> + <form string="Cancel Reason"> + <group> + <field name="reason_cancel" widget="selection" /> + <field name="attachment_bukti" widget="many2many_binary" required="1" /> + <field name="nomor_so_pengganti" + attrs="{'invisible': [('reason_cancel', '!=', 'ganti_quotation')]}" /> + </group> + <footer> + <button string="Confirm" type="object" name="confirm_reject" + class="btn-primary" /> + <button string="Cancel" class="btn-secondary" special="cancel" /> + </footer> + </form> + </field> + </record> - <record id="action_cancel_reason_order" model="ir.actions.act_window"> - <field name="name">Cancel Reason</field> - <field name="res_model">cancel.reason.order</field> - <field name="view_mode">form</field> - <field name="target">new</field> - </record> + <record id="action_cancel_reason_order" model="ir.actions.act_window"> + <field name="name">Cancel Reason</field> + <field name="res_model">cancel.reason.order</field> + <field name="view_mode">form</field> + <field name="target">new</field> + </record> </data> <data> <record id="sale_order_tree_view_inherit" model="ir.ui.view"> <field name="name">Sale Order</field> <field name="model">sale.order</field> - <field name="inherit_id" ref="sale.view_quotation_tree_with_onboarding"/> + <field name="inherit_id" ref="sale.view_quotation_tree_with_onboarding" /> <field name="arch" type="xml"> <field name="state" position="after"> <field name="approval_status" /> - <field name="client_order_ref"/> - <field name="payment_type" optional="hide"/> - <field name="payment_status" optional="hide"/> - <field name="pareto_status" optional="hide"/> - <field name="shipping_method_picking" optional="hide"/> + <field name="client_order_ref" /> + <field name="payment_type" optional="hide" /> + <field name="payment_status" optional="hide" /> + <field name="pareto_status" optional="hide" /> + <field name="shipping_method_picking" optional="hide" /> </field> </field> </record> <record id="sales_order_tree_view_inherit" model="ir.ui.view"> <field name="name">Sale Order</field> <field name="model">sale.order</field> - <field name="inherit_id" ref="sale.view_order_tree"/> + <field name="inherit_id" ref="sale.view_order_tree" /> <field name="arch" type="xml"> <field name="state" position="after"> <field name="approval_status" /> - <field name="client_order_ref"/> - <field name="so_status"/> - <field name="date_status_done"/> - <field name="date_kirim_ril"/> - <field name="date_driver_departure"/> - <field name="date_driver_arrival"/> - <field name="payment_type" optional="hide"/> - <field name="payment_status" optional="hide"/> - <field name="pareto_status" optional="hide"/> + <field name="client_order_ref" /> + <field name="so_status" /> + <field name="date_status_done" /> + <field name="date_kirim_ril" /> + <field name="date_driver_departure" /> + <field name="date_driver_arrival" /> + <field name="payment_type" optional="hide" /> + <field name="payment_status" optional="hide" /> + <field name="pareto_status" optional="hide" /> </field> </field> </record> <record id="sale_order_multi_update_ir_actions_server" model="ir.actions.server"> <field name="name">Mark As Cancel</field> - <field name="model_id" ref="sale.model_sale_order"/> - <field name="binding_model_id" ref="sale.model_sale_order"/> + <field name="model_id" ref="sale.model_sale_order" /> + <field name="binding_model_id" ref="sale.model_sale_order" /> <field name="binding_view_types">form,list</field> <field name="state">code</field> <field name="code">action = records.open_form_multi_update_state()</field> @@ -365,46 +417,81 @@ <record id="sale_order_update_multi_actions_server" model="ir.actions.server"> <field name="name">Mark As Completed</field> - <field name="model_id" ref="sale.model_sale_order"/> - <field name="binding_model_id" ref="sale.model_sale_order"/> + <field name="model_id" ref="sale.model_sale_order" /> + <field name="binding_model_id" ref="sale.model_sale_order" /> <field name="state">code</field> <field name="code">action = records.open_form_multi_update_status()</field> </record> <record id="mail_template_sale_order_web_approve_notification" model="mail.template"> <field name="name">Sale Order: Web Approve Notification</field> - <field name="model_id" ref="indoteknik_custom.model_sale_order"/> + <field name="model_id" ref="indoteknik_custom.model_sale_order" /> <field name="subject">Permintaan Persetujuan Pesanan ${object.name} di Indoteknik.com</field> <field name="email_from">sales@indoteknik.com</field> <field name="email_to">${object.partner_id.email | safe}</field> <field name="email_cc">${object.partner_id.get_approve_partner_ids("email_comma_sep")}</field> <field name="body_html" type="html"> - <table border="0" cellpadding="0" cellspacing="0" style="padding: 16px 0; background-color: #F1F1F1; font-family:Inter, Helvetica, Verdana, Arial,sans-serif; line-height: 24px; color: #454748; width: 100%; border-collapse:separate;"> - <tr><td align="center"> - <table border="0" cellpadding="0" cellspacing="0" width="590" style="font-size: 13px; padding: 16px; background-color: white; color: #454748; border-collapse:separate;"> - <tbody> - <tr> - <td align="center" style="min-width: 590px;"> - <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;"> - <tr><td style="padding-bottom: 24px;">Dear ${(object.partner_id.get_main_parent()).name},</td></tr> + <table border="0" cellpadding="0" cellspacing="0" + style="padding: 16px 0; background-color: #F1F1F1; font-family:Inter, Helvetica, Verdana, Arial,sans-serif; line-height: 24px; color: #454748; width: 100%; border-collapse:separate;"> + <tr> + <td align="center"> + <table border="0" cellpadding="0" cellspacing="0" width="590" + style="font-size: 13px; padding: 16px; background-color: white; color: #454748; border-collapse:separate;"> + <tbody> + <tr> + <td align="center" style="min-width: 590px;"> + <table border="0" cellpadding="0" cellspacing="0" + width="590" + style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;"> + <tr> + <td style="padding-bottom: 24px;"> + Dear + ${(object.partner_id.get_main_parent()).name},</td> + </tr> - <tr><td style="padding-bottom: 16px;">Ini adalah konfirmasi pesanan dari ${object.partner_id.name | safe} untuk nomor pesanan ${object.name} yang memerlukan persetujuan agar dapat diproses.</td></tr> - <tr><td style="padding-bottom: 16px;"> - <a href="https://indoteknik.com/my/quotations/${object.id}" style="color: white; background-color: #C53030; border: none; border-radius: 6px; padding: 4px 8px; width: fit-content; display: block;"> - Lihat Pesanan - </a> - </td></tr> - <tr><td style="padding-bottom: 16px;">Mohon segera melakukan tinjauan terhadap pesanan ini dan memberikan persetujuan. Terima kasih atas perhatian dan kerjasama Anda. Kami berharap dapat segera melanjutkan proses pesanan ini setelah mendapatkan persetujuan Anda.</td></tr> + <tr> + <td style="padding-bottom: 16px;">Ini adalah + konfirmasi pesanan dari + ${object.partner_id.name | safe} untuk nomor + pesanan ${object.name} yang memerlukan + persetujuan agar dapat diproses.</td> + </tr> + <tr> + <td style="padding-bottom: 16px;"> + <a + href="https://indoteknik.com/my/quotations/${object.id}" + style="color: white; background-color: #C53030; border: none; border-radius: 6px; padding: 4px 8px; width: fit-content; display: block;"> + Lihat Pesanan + </a> + </td> + </tr> + <tr> + <td style="padding-bottom: 16px;">Mohon segera + melakukan tinjauan terhadap pesanan ini dan + memberikan persetujuan. Terima kasih atas + perhatian dan kerjasama Anda. Kami berharap + dapat segera melanjutkan proses pesanan ini + setelah mendapatkan persetujuan Anda.</td> + </tr> - <tr><td style="padding-bottom: 2px;">Hormat kami,</td></tr> - <tr><td style="padding-bottom: 2px;">PT. Indoteknik Dotcom Gemilang</td></tr> - <tr><td style="padding-bottom: 2px;">sales@indoteknik.com</td></tr> - </table> - </td> - </tr> - </tbody> - </table> - </td></tr> + <tr> + <td style="padding-bottom: 2px;">Hormat kami,</td> + </tr> + <tr> + <td style="padding-bottom: 2px;">PT. Indoteknik + Dotcom Gemilang</td> + </tr> + <tr> + <td style="padding-bottom: 2px;"> + sales@indoteknik.com</td> + </tr> + </table> + </td> + </tr> + </tbody> + </table> + </td> + </tr> </table> </field> </record> @@ -416,11 +503,11 @@ <field name="model">sales.order.purchase.match</field> <field name="arch" type="xml"> <tree editable="top" create="false" delete="false"> - <field name="purchase_order_id" readonly="1"/> - <field name="purchase_line_id" readonly="1"/> - <field name="product_id" readonly="1"/> - <field name="qty_so" readonly="1"/> - <field name="qty_po" readonly="1"/> + <field name="purchase_order_id" readonly="1" /> + <field name="purchase_line_id" readonly="1" /> + <field name="product_id" readonly="1" /> + <field name="qty_so" readonly="1" /> + <field name="qty_po" readonly="1" /> </tree> </field> </record> @@ -432,9 +519,9 @@ <field name="model">sales.order.koli</field> <field name="arch" type="xml"> <tree editable="top" create="false" delete="false"> - <field name="koli_id" readonly="1"/> - <field name="picking_id" readonly="1"/> - <field name="state" readonly="1"/> + <field name="koli_id" readonly="1" /> + <field name="picking_id" readonly="1" /> + <field name="state" readonly="1" /> </tree> </field> </record> @@ -443,32 +530,32 @@ <data> </data> - <record id="sales_order_fulfillment_v2_tree" model="ir.ui.view"> - <field name="name">sales.order.fulfillment.v2.tree</field> - <field name="model">sales.order.fulfillment.v2</field> - <field name="arch" type="xml"> - <tree editable="top" create="false"> - <field name="product_id" readonly="1"/> - <field name="so_qty" readonly="1" optional="show"/> - <field name="reserved_stock_qty" readonly="1" optional="show"/> - <field name="delivered_qty" readonly="1" optional="hide"/> - <field name="po_ids" widget="many2many_tags" readonly="1" optional="show"/> - <field name="po_qty" readonly="1" optional="show"/> - <field name="received_qty" readonly="1" optional="show"/> - <field name="purchaser" readonly="1" optional="hide"/> - </tree> - </field> - </record> + <record id="sales_order_fulfillment_v2_tree" model="ir.ui.view"> + <field name="name">sales.order.fulfillment.v2.tree</field> + <field name="model">sales.order.fulfillment.v2</field> + <field name="arch" type="xml"> + <tree editable="top" create="false"> + <field name="product_id" readonly="1" /> + <field name="so_qty" readonly="1" optional="show" /> + <field name="reserved_stock_qty" readonly="1" optional="show" /> + <field name="delivered_qty" readonly="1" optional="hide" /> + <field name="po_ids" widget="many2many_tags" readonly="1" optional="show" /> + <field name="po_qty" readonly="1" optional="show" /> + <field name="received_qty" readonly="1" optional="show" /> + <field name="purchaser" readonly="1" optional="hide" /> + </tree> + </field> + </record> <data> <record id="sales_order_fullfillmet_tree" model="ir.ui.view"> <field name="name">sales.order.fullfillment.tree</field> <field name="model">sales.order.fullfillment</field> <field name="arch" type="xml"> <tree editable="top" create="false"> - <field name="product_id" readonly="1"/> - <field name="reserved_from" readonly="1"/> - <field name="qty_fullfillment" readonly="1"/> - <field name="user_id" readonly="1"/> + <field name="product_id" readonly="1" /> + <field name="reserved_from" readonly="1" /> + <field name="qty_fullfillment" readonly="1" /> + <field name="user_id" readonly="1" /> </tree> </field> </record> @@ -480,9 +567,9 @@ <field name="model">sales.order.reject</field> <field name="arch" type="xml"> <tree editable="top" create="false"> - <field name="product_id" readonly="1"/> - <field name="qty_reject" readonly="1"/> - <field name="reason_reject" readonly="1"/> + <field name="product_id" readonly="1" /> + <field name="qty_reject" readonly="1" /> + <field name="reason_reject" readonly="1" /> </tree> </field> </record> @@ -491,8 +578,8 @@ <data> <record id="sale_order_multi_create_uangmuka_ir_actions_server" model="ir.actions.server"> <field name="name">Uang Muka</field> - <field name="model_id" ref="sale.model_sale_order"/> - <field name="binding_model_id" ref="sale.model_sale_order"/> + <field name="model_id" ref="sale.model_sale_order" /> + <field name="binding_model_id" ref="sale.model_sale_order" /> <field name="state">code</field> <field name="code">action = records.open_form_multi_create_uang_muka()</field> </record> @@ -501,66 +588,84 @@ <data> <record id="mail_template_sale_order_notification_to_salesperson" model="mail.template"> <field name="name">Sale Order: Notification to Salesperson</field> - <field name="model_id" ref="sale.model_sale_order"/> + <field name="model_id" ref="sale.model_sale_order" /> <field name="subject">Konsolidasi Pengiriman</field> <field name="email_from">sales@indoteknik.com</field> <field name="email_to">${object.user_id.login | safe}</field> <field name="body_html" type="html"> - <table border="0" cellpadding="0" cellspacing="0" style="padding-top: 16px; background-color: #F1F1F1; font-family:Inter, Helvetica, Verdana, Arial,sans-serif; line-height: 24px; color: #454748; width: 100%; border-collapse:separate;"> - <tr><td align="center"> - <table border="0" cellpadding="0" cellspacing="0" width="590" style="font-size: 13px; padding: 16px; background-color: white; color: #454748; border-collapse:separate;"> - <!-- HEADER --> - <tbody> - <tr> - <td align="center" style="min-width: 590px;"> - <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;"> - <tr> - <td valign="middle"> - <span></span> - </td> - </tr> - </table> - </td> - </tr> - <!-- CONTENT --> - <tr> - <td align="center" style="min-width: 590px;"> - <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;"> - <tr><td style="padding-bottom: 24px;">Dear ${salesperson_name},</td></tr> - - <tr><td style="padding-bottom: 16px;">Terdapat pesanan dari BP ${business_partner} untuk site ${site} dengan total belanja ${sum_total_amount} dari list SO dibawah ini:</td></tr> - - <tr> - <td> - <table border="1" cellpadding="5" cellspacing="0"> - <thead> - <tr> - <th>Nama Pesanan</th> - <th>Nama Perusahaan Induk</th> - <th>Nama Situs</th> - <th>Total Pembelian</th> - </tr> - </thead> - <tbody> - ${table_content} - </tbody> - </table> - </td> - </tr> - - <tr> - <td style="text-align:center;"> - <hr width="100%" - style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin: 16px 0px 16px 0px;" /> - </td> - </tr> - </table> - </td> - </tr> - <!-- CONTENT --> - </tbody> - </table> - </td></tr> + <table border="0" cellpadding="0" cellspacing="0" + style="padding-top: 16px; background-color: #F1F1F1; font-family:Inter, Helvetica, Verdana, Arial,sans-serif; line-height: 24px; color: #454748; width: 100%; border-collapse:separate;"> + <tr> + <td align="center"> + <table border="0" cellpadding="0" cellspacing="0" width="590" + style="font-size: 13px; padding: 16px; background-color: white; color: #454748; border-collapse:separate;"> + <!-- HEADER --> + <tbody> + <tr> + <td align="center" style="min-width: 590px;"> + <table border="0" cellpadding="0" cellspacing="0" + width="590" + style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;"> + <tr> + <td valign="middle"> + <span></span> + </td> + </tr> + </table> + </td> + </tr> + <!-- CONTENT --> + <tr> + <td align="center" style="min-width: 590px;"> + <table border="0" cellpadding="0" cellspacing="0" + width="590" + style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;"> + <tr> + <td style="padding-bottom: 24px;">Dear + ${salesperson_name},</td> + </tr> + + <tr> + <td style="padding-bottom: 16px;">Terdapat + pesanan dari BP ${business_partner} untuk + site ${site} dengan total belanja + ${sum_total_amount} dari list SO dibawah + ini:</td> + </tr> + + <tr> + <td> + <table border="1" cellpadding="5" + cellspacing="0"> + <thead> + <tr> + <th>Nama Pesanan</th> + <th>Nama Perusahaan Induk</th> + <th>Nama Situs</th> + <th>Total Pembelian</th> + </tr> + </thead> + <tbody> + ${table_content} + </tbody> + </table> + </td> + </tr> + + <tr> + <td style="text-align:center;"> + <hr width="100%" + style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin: 16px 0px 16px 0px;" /> + </td> + </tr> + </table> + </td> + </tr> + <!-- CONTENT --> + </tbody> + </table> + </td> + </tr> </table> </field> </record> diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 4c60a496..c916f2ef 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -123,6 +123,7 @@ <field name="date_doc_kirim" attrs="{'readonly':[('invoice_status', '=', 'invoiced')]}"/> <field name="summary_qty_operation"/> <field name="count_line_operation"/> + <field name="linked_manual_bu_out" attrs="{'invisible': [('location_id', '=', 60)]}" domain="[('picking_type_code', '=', 'outgoing'),('state', 'not in', ['done','cancel']), ('group_id', '=', group_id)]"/> <field name="account_id" attrs="{ 'readonly': [['state', 'in', ['done', 'cancel']]], @@ -174,6 +175,7 @@ <group> <field name="notee"/> <field name="note_logistic"/> + <field name="note_info"/> <field name="responsible" /> <field name="carrier_id"/> <field name="out_code" attrs="{'invisible': [['out_code', '=', False]]}"/> @@ -243,7 +245,8 @@ <field name="model">scan.koli</field> <field name="arch" type="xml"> <tree editable="bottom"> - <field name="koli_id" options="{'no_create': True}" required="1" domain="[('state', '=', 'not_delivered')]"/> + <field name="code_koli"/> + <field name="koli_id" options="{'no_create': True}" domain="[('state', '=', 'not_delivered')]"/> <field name="scan_koli_progress"/> </tree> </field> @@ -254,7 +257,7 @@ <field name="model">konfirm.koli</field> <field name="arch" type="xml"> <tree editable="bottom"> - <field name="pick_id" options="{'no_create': True}" required="1" domain="[('picking_type_code', '=', 'internal'), ('group_id', '=', parent.group_id)]"/> + <field name="pick_id" options="{'no_create': True}" required="1" domain="[('picking_type_code', '=', 'internal'), ('group_id', '=', parent.group_id), ('linked_manual_bu_out', '=', parent.id)]"/> </tree> </field> </record> @@ -276,8 +279,9 @@ <field name="model">check.product</field> <field name="arch" type="xml"> <tree editable="bottom" decoration-warning="status == 'Pending'" decoration-success="status == 'Done'"> - <field name="product_id" required="1" options="{'no_create': True}"/> - <field name="quantity" readonly="1"/> + <field name="code_product"/> + <field name="product_id"/> + <field name="quantity"/> <field name="status" readonly="1"/> </tree> </field> diff --git a/indoteknik_custom/views/vendor_payment_term.xml b/indoteknik_custom/views/vendor_payment_term.xml index e0e96388..7d16b129 100644 --- a/indoteknik_custom/views/vendor_payment_term.xml +++ b/indoteknik_custom/views/vendor_payment_term.xml @@ -8,6 +8,8 @@ <field name="display_name"/> <field name="name"/> <field name="parent_id"/> + <field name="minimum_amount"/> + <field name="minimum_amount_tax"/> <field name="property_supplier_payment_term_id"/> </tree> </field> @@ -23,6 +25,8 @@ <group> <field name="name"/> <field name="parent_id" readonly="1"/> + <field name="minimum_amount"/> + <field name="minimum_amount_tax"/> <field name="property_supplier_payment_term_id"/> </group> </group> |
