diff options
23 files changed, 496 insertions, 166 deletions
diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index accc7531..1a75c830 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -198,7 +198,7 @@ class SaleOrder(controller.Controller): if status in ['dikemas', 'dikirim', 'selesai', 'partial']: filtered_orders = [] for sale_order in sale_orders: - bu_pickings = [p for p in sale_order.picking_ids if p.picking_type_id and p.picking_type_id.id == 29] + bu_pickings = [p for p in sale_order.picking_ids if p.picking_type_id and p.picking_type_id.id == 29 and p.state != 'cancel'] total = len(bu_pickings) done_pickings = [p for p in bu_pickings if p.state == 'done'] done_with_driver = [p for p in done_pickings if p.sj_return_date] diff --git a/indoteknik_api/models/sale_order.py b/indoteknik_api/models/sale_order.py index c59dead9..23be358a 100644 --- a/indoteknik_api/models/sale_order.py +++ b/indoteknik_api/models/sale_order.py @@ -75,7 +75,7 @@ class SaleOrder(models.Model): if sale_order.state == 'sale': bu_pickings = [ p for p in sale_order.picking_ids - if p.picking_type_id and p.picking_type_id.id == 29 + if p.picking_type_id and p.picking_type_id.id == 29 and p.state != 'cancel' ] # Hitung status masing-masing picking diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index f2145d0c..806d5706 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -183,6 +183,7 @@ 'views/letter_receivable.xml', 'views/letter_receivable_mail_template.xml' # 'views/reimburse.xml', + 'views/sj_tele.xml' ], 'demo': [], 'css': [], diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index c8910669..6dc61277 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -158,4 +158,5 @@ from . import tukar_guling from . import tukar_guling_po from . import update_date_planned_po_wizard from . import unpaid_invoice_view -from . import letter_receivable
\ No newline at end of file +from . import letter_receivable +from . import sj_tele diff --git a/indoteknik_custom/models/logbook_sj.py b/indoteknik_custom/models/logbook_sj.py index 75b2622f..0cda9c8b 100644 --- a/indoteknik_custom/models/logbook_sj.py +++ b/indoteknik_custom/models/logbook_sj.py @@ -24,6 +24,7 @@ class LogbookSJ(models.TransientModel): } report_logbook = self.env['report.logbook.sj'].create([parameters_header]) + seq=1 for line in logbook_line: picking = self.env['stock.picking'].search([('picking_code', '=', line.name)], limit=1) if not picking: @@ -43,9 +44,11 @@ class LogbookSJ(models.TransientModel): 'tracking_no': stock.delivery_tracking_no, 'partner_id': parent_id, 'report_logbook_sj_id': report_logbook.id, - 'note': line.note + 'note': line.note, + 'line_num': seq } self.env['report.logbook.sj.line'].create([data]) + seq += 1 report_logbook_ids.append(report_logbook.id) line.unlink() diff --git a/indoteknik_custom/models/manufacturing.py b/indoteknik_custom/models/manufacturing.py index aea01362..f986fd4f 100644 --- a/indoteknik_custom/models/manufacturing.py +++ b/indoteknik_custom/models/manufacturing.py @@ -4,54 +4,56 @@ import logging _logger = logging.getLogger(__name__) + class Manufacturing(models.Model): _inherit = 'mrp.production' unbuild_counter = fields.Integer(string='Unbuild Counter', default=0, help='For restrict unbuild more than once') - + def action_confirm(self): if self._name != 'mrp.production': return super(Manufacturing, self).action_confirm() if not self.env.user.is_purchasing_manager: raise UserError("Hanya bisa di confirm oleh Purchasing Manager") - + # if self.location_src_id.id != 75: # raise UserError('Component Location hanya bisa di AS/Stock') # elif self.location_dest_id.id != 75: # raise UserError('Finished Product Location hanya bisa di AS/Stock') - + result = super(Manufacturing, self).action_confirm() return result - + def button_mark_done(self): if self._name != 'mrp.production': return super(Manufacturing, self).button_mark_done() # Check product category if self.product_id.categ_id.name != 'Finish Good': raise UserError('Tidak bisa di complete karna product category bukan Unit / Finish Good') - + if self.sale_order and self.sale_order.state != 'sale': raise UserError( ('Tidak bisa Mark as Done.\nSales Order "%s" (Nomor: %s) belum dikonfirmasi.') % (self.sale_order.partner_id.name, self.sale_order.name) ) - + for line in self.move_raw_ids: # if line.quantity_done > 0 and line.quantity_done != self.product_uom_qty: # raise UserError('Qty Consume per Line tidak sama dengan Qty to Produce') if line.forecast_availability != line.product_uom_qty: - raise UserError('Qty Reserved belum sesuai dengan yang seharusnya, product: %s' % line.product_id.display_name) + raise UserError( + 'Qty Reserved belum sesuai dengan yang seharusnya, product: %s' % line.product_id.display_name) result = super(Manufacturing, self).button_mark_done() return result - + def button_unbuild(self): if self._name != 'mrp.production': return super(Manufacturing, self).button_unbuild() - + if self.unbuild_counter >= 1: raise UserError('Tidak bisa unbuild lebih dari 1 kali') - + self.unbuild_counter = self.unbuild_counter + 1 result = super(Manufacturing, self).button_unbuild() diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 18811b85..68180235 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -6,6 +6,7 @@ import logging from pytz import timezone, utc import io import base64 +from odoo.tools import lazy_property try: from odoo.tools.misc import xlsxwriter except ImportError: @@ -115,6 +116,20 @@ class PurchaseOrder(models.Model): compute='_compute_complete_bu_in_count' ) + show_description = fields.Boolean( + string='Show Description', + default=True + ) + + @api.onchange('show_description') + def onchange_show_description(self): + if self.show_description == True: + for line in self.order_line: + line.show_description = True + else: + for line in self.order_line: + line.show_description = False + def _compute_complete_bu_in_count(self): for order in self: if order.state not in ['done', 'cancel']: @@ -137,7 +152,7 @@ class PurchaseOrder(models.Model): def _compute_date_planned(self): """ date_planned = the earliest date_planned across all order lines. """ for order in self: - order.date_planned = False + order.date_planned = order.date_planned @api.constrains('date_planned') def constrains_date_planned(self): @@ -1066,8 +1081,11 @@ class PurchaseOrder(models.Model): # sticky=True # ) + has_bom = self.product_bom_id.id + has_manufacturing = self.manufacturing_id.id + if not self.from_apo: - if not self.matches_so and not self.env.user.is_purchasing_manager and not self.env.user.is_leader: + if not self.matches_so and not self.env.user.is_purchasing_manager and not self.env.user.is_leader and not has_bom and not has_manufacturing: raise UserError("Tidak ada link dengan SO, harus di confirm oleh Purchasing Manager") send_email = False diff --git a/indoteknik_custom/models/purchase_order_line.py b/indoteknik_custom/models/purchase_order_line.py index 315795d5..a3c3a33b 100755 --- a/indoteknik_custom/models/purchase_order_line.py +++ b/indoteknik_custom/models/purchase_order_line.py @@ -50,6 +50,7 @@ class PurchaseOrderLine(models.Model): cost_service_per_item = fields.Float(string='Biaya Jasa Per Item', compute='_compute_doc_delivery_amt') contribution_cost_service = fields.Float(string='Contribution Cost Service', compute='_compute_doc_delivery_amt') ending_price = fields.Float(string='Ending Price', compute='_compute_doc_delivery_amt') + show_description = fields.Boolean(string='Show Description', help="Show Description when print po", default=True) def _compute_doc_delivery_amt(self): for line in self: diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py index f511ed5d..4ee76006 100644 --- a/indoteknik_custom/models/refund_sale_order.py +++ b/indoteknik_custom/models/refund_sale_order.py @@ -77,7 +77,7 @@ class RefundSaleOrder(models.Model): 'account.move', string="Journal Payment", copy=False, - help="Pilih transaksi salah transfer dari jurnal Uang Muka (journal_id=11) yang tidak terkait SO." + help="Pilih transaksi salah transfer dari jurnal Uang Muka yang tidak terkait SO." ) tukar_guling_count = fields.Integer( @@ -113,7 +113,7 @@ class RefundSaleOrder(models.Model): string='Customer', required=True ) - advance_move_names = fields.Html(string="Group Journal SO", compute="_compute_advance_move_names") + advance_move_names = fields.Html(string="Group Journal Payment", compute="_compute_advance_move_names") uang_masuk_type = fields.Selection([ ('pdf', 'PDF'), ('image', 'Image'), @@ -237,6 +237,7 @@ class RefundSaleOrder(models.Model): refund_type = vals.get('refund_type') invoice_ids_data = vals.get('invoice_ids', []) invoice_ids = invoice_ids_data[0][2] if invoice_ids_data and invoice_ids_data[0][0] == 6 else [] + invoices = self.env['account.move'].browse(invoice_ids) if invoice_ids and refund_type and refund_type not in ['uang', 'barang_kosong_sebagian', 'barang_kosong', 'retur_half']: raise UserError("Refund type Hanya Bisa Lebih Bayar, Barang Kosong Sebagian, atau Retur jika ada invoice") @@ -276,18 +277,46 @@ class RefundSaleOrder(models.Model): ('journal_id', '=', 11), ('state', '=', 'posted'), ]) - total_uang_muka = sum(moves.mapped('amount_total_signed')) if moves else 0.0 - total_midtrans = sum(self.env['sale.order'].browse(so_ids).mapped('gross_amount')) if so_ids else 0.0 - total_pembayaran = total_uang_muka + total_midtrans + piutangbca = self.env['account.move'].search([ + ('ref', 'in', invoices.mapped('name')), + ('journal_id', '=', 4), + ('state', '=', 'posted'), + ]) + piutangmdr = self.env['account.move'].search([ + ('ref', 'in', invoices.mapped('name')), + ('journal_id', '=', 7), + ('state', '=', 'posted'), + ]) + has_moves = bool(moves) + has_piutangmdr = bool(piutangmdr) + has_piutangbca = bool(piutangbca) + ssos = self.env['sale.order'].browse(so_ids) + has_settlement = any(so.payment_status == 'settlement' for so in ssos) + + sisa_uang_masuk = 0.0 + if has_moves and has_settlement: + sisa_uang_masuk = sum(moves.mapped('amount_total_signed')) + sum(ssos.mapped('gross_amount')) + elif has_moves: + sisa_uang_masuk = sum(moves.mapped('amount_total_signed')) + elif has_settlement: + sisa_uang_masuk = sum(ssos.mapped('gross_amount')) + elif has_piutangbca: + sisa_uang_masuk = sum(piutangbca.mapped('amount_total_signed')) + elif has_piutangmdr: + sisa_uang_masuk = sum(piutangmdr.mapped('amount_total_signed')) + else: + raise UserError( + "❌ Tidak bisa melakukan refund karena SO tidak memiliki Record Uang Masuk " + "(Journal Uang Muka / Payment Invoices / Midtrans Payment)." + ) + existing_refunds = self.env['refund.sale.order'].search([ ('sale_order_ids', 'in', so_ids) ], order='id desc', limit=1) if existing_refunds: sisa_uang_masuk = existing_refunds.remaining_refundable - else: - sisa_uang_masuk = total_pembayaran if sisa_uang_masuk < 0: raise UserError("❌ Tidak ada sisa transaksi untuk di-refund.") @@ -297,6 +326,14 @@ class RefundSaleOrder(models.Model): total_invoice = sum(self.env['account.move'].browse(invoice_ids).mapped('amount_total_signed')) if invoice_ids else 0.0 vals['total_invoice'] = total_invoice amount_refund = vals.get('amount_refund', 0.0) + can_refund = sisa_uang_masuk - total_invoice + + if amount_refund > can_refund or can_refund == 0.0: + raise ValidationError( + _("Maksimal refund yang bisa dilakukan adalah sebesar %s. " + "Silakan sesuaikan jumlah refund.") % (can_refund) + ) + if amount_refund <= 0.00: raise ValidationError('Total Refund harus lebih dari 0 jika ingin mengajukan refund') @@ -393,9 +430,16 @@ class RefundSaleOrder(models.Model): total_invoice = sum(self.env['account.move'].browse(invoice_ids).mapped('amount_total_signed')) vals['total_invoice'] = total_invoice uang_masuk = rec.uang_masuk + can_refund = uang_masuk - total_invoice amount_refund = vals.get('amount_refund', rec.amount_refund) + if amount_refund > can_refund: + raise ValidationError( + _("Maksimal refund yang bisa dilakukan adalah sebesar %s. " + "Silakan sesuaikan jumlah refund.") % (can_refund) + ) + if amount_refund <= 0: raise ValidationError("Total Refund harus lebih dari 0.") @@ -437,15 +481,34 @@ class RefundSaleOrder(models.Model): def _compute_advance_move_names(self): for rec in self: move_links = [] + + invoice_ids = rec.sale_order_ids.mapped('invoice_ids') + moves = self.env['account.move'].search([ ('sale_id', 'in', rec.sale_order_ids.ids), ('journal_id', '=', 11), - ('state', '=', 'posted') + ('state', '=', 'posted'), + ]) + + piutangbca = self.env['account.move'].search([ + ('ref', 'in', invoice_ids.mapped('name')), + ('journal_id', '=', 4), + ('state', '=', 'posted'), + ]) + + piutangmdr = self.env['account.move'].search([ + ('ref', 'in', invoice_ids.mapped('name')), + ('journal_id', '=', 7), + ('state', '=', 'posted'), ]) - for move in moves: + + all_moves = moves | piutangbca | piutangmdr + + for move in all_moves: url = f"/web#id={move.id}&model=account.move&view_type=form" name = html_escape(move.name or 'Unnamed') move_links.append(f'<a href="{url}" target="_blank">{name}</a>') + rec.advance_move_names = ', '.join(move_links) if move_links else "-" @api.depends('sale_order_ids.user_id') @@ -481,9 +544,35 @@ class RefundSaleOrder(models.Model): ('journal_id', '=', 11), ('state', '=', 'posted'), ]) - total_uang_muka = sum(moves.mapped('amount_total_signed')) if moves else 0.0 - total_midtrans = sum(self.env['sale.order'].browse(so_ids).mapped('gross_amount')) if so_ids else 0.0 - self.uang_masuk = (total_uang_muka + total_midtrans) - amount_refund_before + piutangbca = self.env['account.move'].search([ + ('ref', 'in', all_invoices.mapped('name')), + ('journal_id', '=', 4), + ('state', '=', 'posted'), + ]) + piutangmdr = self.env['account.move'].search([ + ('ref', 'in', all_invoices.mapped('name')), + ('journal_id', '=', 7), + ('state', '=', 'posted'), + ]) + has_moves = bool(moves) + has_piutangmdr = bool(piutangmdr) + has_piutangbca = bool(piutangbca) + ssos = self.env['sale.order'].browse(so_ids) + has_settlement = any(so.payment_status == 'settlement' for so in ssos) + + sisa_uang_masuk = 0.0 + if has_moves and has_settlement: + sisa_uang_masuk = sum(moves.mapped('amount_total_signed')) + sum(ssos.mapped('gross_amount')) + elif has_moves: + sisa_uang_masuk = sum(moves.mapped('amount_total_signed')) + elif has_settlement: + sisa_uang_masuk = sum(ssos.mapped('gross_amount')) + elif has_piutangbca: + sisa_uang_masuk = sum(piutangbca.mapped('amount_total_signed')) + elif has_piutangmdr: + sisa_uang_masuk = sum(piutangmdr.mapped('amount_total_signed')) + + self.uang_masuk = sisa_uang_masuk - amount_refund_before self.invoice_ids = all_invoices @@ -504,7 +593,6 @@ class RefundSaleOrder(models.Model): """ Validasi SO harus punya uang masuk (Journal Uang Muka / Midtrans) """ for rec in self: invalid_orders = [] - total_uang_masuk = 0.0 for so in rec.sale_order_ids: # cari journal uang muka @@ -513,21 +601,24 @@ class RefundSaleOrder(models.Model): ('journal_id', '=', 11), # Journal Uang Muka ('state', '=', 'posted'), ]) + piutangbca = self.env['account.move'].search([ + ('ref', 'in', rec.invoice_ids.mapped('name')), + ('journal_id', '=', 4), + ('state', '=', 'posted'), + ]) + piutangmdr = self.env['account.move'].search([ + ('ref', 'in', rec.invoice_ids.mapped('name')), + ('journal_id', '=', 7), + ('state', '=', 'posted'), + ]) - if not moves and so.payment_status != 'settlement': + if not moves and so.payment_status != 'settlement' and not piutangbca and not piutangmdr: invalid_orders.append(so.name) - if moves: - total_uang_muka = sum(moves.mapped('amount_total_signed')) or 0.0 - total_uang_masuk += total_uang_muka - else: - # fallback Midtrans gross_amount - total_uang_masuk += so.gross_amount or 0.0 - if invalid_orders: raise ValidationError( f"Tidak dapat membuat refund untuk SO {', '.join(invalid_orders)} " - "karena tidak memiliki Record Uang Masuk (Journal Uang Muka/Midtrans).\n" + "karena tidak memiliki Record Uang Masuk (Journal Uang Muka/Payment Invoice/Midtrans).\n" "Pastikan semua SO yang dipilih sudah memiliki Record pembayaran yang valid." ) diff --git a/indoteknik_custom/models/report_logbook_sj.py b/indoteknik_custom/models/report_logbook_sj.py index 17119c12..3b07ff02 100644 --- a/indoteknik_custom/models/report_logbook_sj.py +++ b/indoteknik_custom/models/report_logbook_sj.py @@ -1,7 +1,14 @@ +from operator import index + from odoo import models, fields, api from odoo.exceptions import UserError from pytz import timezone from datetime import datetime +import requests +import json +import logging + +_logger = logging.getLogger(__name__) class ReportLogbookSJ(models.Model): _name = 'report.logbook.sj' @@ -60,9 +67,28 @@ class ReportLogbookSJ(models.Model): self.state = 'terima_semua' else: raise UserError('Hanya Accounting yang bisa Approve') - + + + def write(self, vals): + res = super(ReportLogbookSJ, self).write(vals) + if 'report_logbook_sj_line' in vals or any(f in vals for f in ()): + self._resequence_lines() + return res + + def _resequence_lines(self): + for rec in self: + lines = rec.report_logbook_sj_line.sorted(key=lambda l: (l.line_num or 0, l.id)) + for idx, line in enumerate(lines, start=1): + if line.line_num != idx: + line.line_num = idx + + @api.onchange('report_logbook_sj_line') + def _onchange_report_logbook_sj_line(self): + self._resequence_lines() + class ReportLogbookSJLine(models.Model): _name = 'report.logbook.sj.line' + _order = 'sequence, id' # urut default di UI & ORM (drag pakai sequence) name = fields.Char(string='SJ Number') driver_id = fields.Many2one(comodel_name='res.users', string='Driver') @@ -70,10 +96,41 @@ class ReportLogbookSJLine(models.Model): arrival_date = fields.Char(string='Arrival Date') carrier_id = fields.Many2one('delivery.carrier', string='Shipping Method') tracking_no = fields.Char(string='Tracking No') - logbook_sj_id = fields.Many2one('report.logbook.sj', string='Logbook SJ') # Corrected model name + + # NOTE: field ini duplikat relasi; pakai salah satu saja. + # kamu boleh hapus logbook_sj_id kalau tidak dipakai di tempat lain. + logbook_sj_id = fields.Many2one('report.logbook.sj', string='Logbook SJ') + partner_id = fields.Many2one('res.partner', string='Customer') picking_id = fields.Many2one('stock.picking', string='Picking') sale_id = fields.Many2one('sale.order', string='Sale Order') + report_logbook_sj_id = fields.Many2one('report.logbook.sj', string='Logbook SJ') not_exist = fields.Boolean(string='Not Exist') note = fields.Char(string='Note') + + sequence = fields.Integer(string='Sequence', default=0, index=True) + + line_num = fields.Integer(string='No', compute='_compute_line_num', store=False) + + @api.depends( + 'report_logbook_sj_id.report_logbook_sj_line', + 'report_logbook_sj_id.report_logbook_sj_line.sequence' + ) + def _compute_line_num(self): + for parent in self.mapped('report_logbook_sj_id'): + lines = parent.report_logbook_sj_line.sorted(key=lambda l: (l.sequence or 0, l.id)) + for i, l in enumerate(lines, start=1): + l.line_num = i + for rec in self.filtered(lambda r: not r.report_logbook_sj_id): + rec.line_num = 0 + + @api.model + def create(self, vals): + if not vals.get('sequence') and vals.get('report_logbook_sj_id'): + last = self.search( + [('report_logbook_sj_id', '=', vals['report_logbook_sj_id'])], + order='sequence desc, id desc', limit=1 + ) + vals['sequence'] = (last.sequence or 0) + 1 + return super().create(vals) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 8f49b579..484a9016 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1674,7 +1674,7 @@ class SaleOrder(models.Model): rec.expected_ready_to_ship = eta_date @api.depends("order_line.product_id", "date_order") - def _compute_etrts_date(self): # Function to calculate Estimated Ready To Ship Date + def _compute_etrts_date(self): self._calculate_etrts_date() @@ -1844,9 +1844,9 @@ class SaleOrder(models.Model): def override_allow_create_invoice(self): if not self.env.user.is_accounting: raise UserError('Hanya Finance Accounting yang dapat klik tombol ini') - for term in self.payment_term_id.line_ids: - if term.days > 0: - raise UserError('Hanya dapat digunakan pada Cash Before Delivery') + # for term in self.payment_term_id.line_ids: + # if term.days > 0: + # raise UserError('Hanya dapat digunakan pada Cash Before Delivery') for line in self.order_line: line.qty_to_invoice = line.product_uom_qty @@ -3261,16 +3261,31 @@ class SaleOrder(models.Model): f"❌ SO {self.name} Belum melakukan kirim barang " f"({', '.join(not_done_pickings.mapped('name'))}). Selesaikan Pengiriman untuk melakukan refund." ) + + invoice_ids = self.invoice_ids.filtered(lambda inv: inv.state != 'cancel') + moves = self.env['account.move'].search([ ('sale_id', '=', self.id), ('journal_id', '=', 11), ('state', '=', 'posted'), ]) + piutangbca = self.env['account.move'].search([ + ('ref', 'in', invoice_ids.mapped('name')), + ('journal_id', '=', 4), + ('state', '=', 'posted'), + ]) + piutangmdr = self.env['account.move'].search([ + ('ref', 'in', invoice_ids.mapped('name')), + ('journal_id', '=', 7), + ('state', '=', 'posted'), + ]) # Default 0 total_uang_muka = 0.0 has_moves = bool(moves) + has_piutangmdr = bool(piutangmdr) + has_piutangbca = bool(piutangbca) has_settlement = self.payment_status == 'settlement' if has_moves and has_settlement: @@ -3279,12 +3294,15 @@ class SaleOrder(models.Model): total_uang_muka = sum(moves.mapped('amount_total_signed')) elif has_settlement: total_uang_muka = self.gross_amount + elif has_piutangbca: + total_uang_muka = sum(piutangbca.mapped('amount_total_signed')) + elif has_piutangmdr: + total_uang_muka = sum(piutangmdr.mapped('amount_total_signed')) else: raise UserError( "Tidak bisa melakukan refund karena SO tidak memiliki Record Uang Masuk " - "(Journal Uang Muka/Midtrans Payment)." + "(Journal Uang Muka/Payment Invoices/Midtrans Payment)." ) - invoice_ids = self.invoice_ids.filtered(lambda inv: inv.state != 'cancel') total_refunded = sum(self.refund_ids.mapped('amount_refund')) sisa_uang_muka = total_uang_muka - total_refunded diff --git a/indoteknik_custom/models/sj_tele.py b/indoteknik_custom/models/sj_tele.py new file mode 100644 index 00000000..d44aa338 --- /dev/null +++ b/indoteknik_custom/models/sj_tele.py @@ -0,0 +1,102 @@ +from odoo import models, fields, api +from odoo.exceptions import UserError +import requests +import json +import logging, subprocess +import time +from collections import OrderedDict + +_logger = logging.getLogger(__name__) + +class SjTele(models.Model): + _name = 'sj.tele' + _description = 'sj.tele' + + picking_id = fields.Many2one('stock.picking', string='Picking') + sale_id = fields.Many2one('sale.order', string='Sales Order') + picking_name = fields.Char(string='Picking Name') + sale_name = fields.Char(string='Sale Name') + create_date = fields.Datetime(string='Create Date') + date_doc_kirim = fields.Datetime(string='Tanggal Kirim SJ') + + # @api.model + # def run_pentaho_carte(self): + # carte = "http://127.0.0.1:8080" + # job_kjb = r"C:/Users/Indoteknik/Desktop/tes.kjb" + # params = {"job": job_kjb, "level": "Basic", "block": "Y"} + # try: + # r = requests.get( + # f"{carte}/kettle/executeJob/", + # params=params, + # auth=("cluster", "cluster"), + # timeout=900, + # ) + # r.raise_for_status() + # # kalau Carte mengembalikan <result>ERROR</result>, anggap gagal + # if "<result>ERROR</result>" in r.text: + # raise UserError(f"Carte error: {r.text}") + # except Exception as e: + # _logger.exception("Carte call failed: %s", e) + # raise UserError(f"Gagal memanggil Carte: {e}") + + # time.sleep(3) + + # self.env['sj.tele'].sudo().woi() + + # return True + + def woi(self): + bot_mqdd = '8203414501:AAHy_XwiUAVrgRM2EJzW7sZx9npRLITZpb8' + chat_id_mqdd = '-1003087280519' + api_base = f'https://api.telegram.org/bot{bot_mqdd}' + + data = self.search([], order='create_date asc', limit=15) + + if not data: + text = "Berikut merupakan nomor BU/OUT yang belum ada di Logbook SJ report:\n✅ tidak ada data (semua sudah tercatat)." + try: + r = requests.post(api_base + "/sendMessage", + json={'chat_id': chat_id_mqdd, 'text': text}, + timeout=20) + r.raise_for_status() + except Exception as e: + _logger.exception("Gagal kirim Telegram (no data): %s", e) + return True + + + lines = [] + groups = OrderedDict() + + for rec in data: + name = rec.picking_name or (rec.picking_id.name if rec.picking_id else '') + pid = rec.picking_id.id if rec.picking_id else '' + so = rec.sale_id.name or rec.sale_name or '' + dttm = (rec.picking_id.date_doc_kirim if (rec.picking_id and rec.picking_id.date_doc_kirim) + else getattr(rec, 'date_doc_kirim', None)) + + # format header tanggal (string), tanpa konversi Waktu/WIB + if dttm: + date_header = dttm if isinstance(dttm, str) else fields.Datetime.to_string(dttm) + date_header = date_header[:10] + else: + date_header = '(Tidak ada tanggal kirim SJ)' + + if name: + groups.setdefault(date_header, []).append(f"- ({pid}) - {name} - {so}") + + # build output berurutan per tanggal + for header_date, items in groups.items(): + lines.append(header_date) + lines.extend(items) + + + header = "Berikut merupakan nomor BU/OUT yang belum ada di Logbook SJ report:\n" + text = header + "\n".join(lines) + + try: + r = requests.post(api_base + "/sendMessage", + json={'chat_id': chat_id_mqdd, 'text': text}) + r.raise_for_status() + except Exception as e: + _logger.exception("Gagal kirim Telegram: %s", e) + return True
\ No newline at end of file diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index a48e0ed1..78a49ee4 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1743,27 +1743,37 @@ class StockPicking(models.Model): } if self.biteship_id: - histori = self.get_manifest_biteship() - day_start = order.estimated_arrival_days_start - day_end = order.estimated_arrival_days - if sale_order_delay: - if sale_order_delay.status == 'delayed': - day_start = day_start + sale_order_delay.days_delayed - day_end = day_end + sale_order_delay.days_delayed - elif sale_order_delay.status == 'early': - day_start = day_start - sale_order_delay.days_delayed - day_end = day_end - sale_order_delay.days_delayed - - eta_start = order.date_order + timedelta(days=day_start) - eta_end = order.date_order + timedelta(days=day_end) - formatted_eta = f"{eta_start.strftime('%d %b')} - {eta_end.strftime('%d %b %Y')}" - response['eta'] = formatted_eta - response['manifests'] = histori.get("manifests", []) - response['delivered'] = histori.get("delivered", - False) or self.sj_return_date != False or self.driver_arrival_date != False - response['status'] = self._map_status_biteship(histori.get("delivered")) + try: + histori = self.get_manifest_biteship() + day_start = order.estimated_arrival_days_start + day_end = order.estimated_arrival_days + if sale_order_delay: + if sale_order_delay.status == 'delayed': + day_start += sale_order_delay.days_delayed + day_end += sale_order_delay.days_delayed + elif sale_order_delay.status == 'early': + day_start -= sale_order_delay.days_delayed + day_end -= sale_order_delay.days_delayed + + eta_start = order.date_order + timedelta(days=day_start) + eta_end = order.date_order + timedelta(days=day_end) + formatted_eta = f"{eta_start.strftime('%d %b')} - {eta_end.strftime('%d %b %Y')}" + + response['eta'] = formatted_eta + response['manifests'] = histori.get("manifests", []) + response['delivered'] = ( + histori.get("delivered", False) + or self.sj_return_date != False + or self.driver_arrival_date != False + ) + response['status'] = self._map_status_biteship(histori.get("delivered")) - return response + return response + + except Exception as e: + # Kalau ada error di biteship, log dan fallback ke Odoo + _logger.warning("Biteship error pada DO %s: %s", self.name, str(e)) + # biarkan lanjut ke kondisi di bawah (pakai Odoo waybill_id) if not self.waybill_id or len(self.waybill_id.manifest_ids) == 0: response['delivered'] = self.sj_return_date != False or self.driver_arrival_date != False diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index f435dd9f..c683f75a 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -61,7 +61,7 @@ class TukarGuling(models.Model): notes = fields.Text('Notes') return_type = fields.Selection(String='Return Type', selection=[ ('tukar_guling', 'Tukar Guling'), # -> barang yang sama - ('revisi_so', 'Revisi SO')], required=True, tracking=3) + ('retur_so', 'Retur SO')], required=True, tracking=3, help='Retur SO (ORT-SRT),\n Tukar Guling (ORT-SRT-PICK-OUT)') state = fields.Selection(string='Status', selection=[ ('draft', 'Draft'), ('approval_sales', ' Approval Sales'), @@ -169,7 +169,7 @@ class TukarGuling(models.Model): raise UserError("❌ Picking type harus BU/OUT atau BU/PICK") for rec in self: if rec.operations and rec.operations.picking_type_id.id == 30: - rec.return_type = 'revisi_so' + rec.return_type = 'retur_so' if self.operations: from_return_picking = self.env.context.get('from_return_picking', False) or \ @@ -315,7 +315,7 @@ class TukarGuling(models.Model): @api.constrains('return_type', 'operations') def _check_required_bu_fields(self): for record in self: - if record.return_type in ['revisi_so', 'tukar_guling'] and not record.operations: + if record.return_type in ['retur_so', 'tukar_guling'] and not record.operations: raise ValidationError("Operations harus diisi") @api.constrains('line_ids', 'state') @@ -352,16 +352,16 @@ class TukarGuling(models.Model): # ('state', '!=', 'cancel') # ]) > 0 - # def _check_invoice_on_revisi_so(self): + # def _check_invoice_on_retur_so(self): # for record in self: - # if record.return_type == 'revisi_so' and record.origin: + # if record.return_type == 'retur_so' and record.origin: # invoices = self.env['account.move'].search([ # ('invoice_origin', 'ilike', record.origin), # ('state', 'not in', ['draft', 'cancel']) # ]) # if invoices: # raise ValidationError( - # _("Tidak bisa memilih Return Type 'Revisi SO' karena dokumen %s sudah dibuat invoice.") % record.origin + # _("Tidak bisa memilih Return Type 'Retur SO' karena dokumen %s sudah dibuat invoice.") % record.origin # ) @@ -414,7 +414,7 @@ class TukarGuling(models.Model): self.ensure_one() if self.operations.picking_type_id.id not in [29, 30]: raise UserError("❌ Picking type harus BU/OUT atau BU/PICK") - # self._check_invoice_on_revisi_so() + # self._check_invoice_on_retur_so() operasi = self.operations.picking_type_id.id tipe = self.return_type pp = vals.get('return_type', tipe) @@ -492,14 +492,14 @@ class TukarGuling(models.Model): self.ensure_one() self._check_not_allow_tukar_guling_on_bu_pick() - # existing_tukar_guling = self.env['tukar.guling'].search([ - # ('operations', '=', self.operations.id), - # ('id', '!=', self.id), - # ('state', '!=', 'cancel'), - # ], limit=1) - # - # if existing_tukar_guling: - # raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) + existing_tukar_guling = self.env['tukar.guling'].search([ + ('operations', '=', self.operations.id), + ('id', '!=', self.id), + ('state', '!=', 'cancel'), + ], limit=1) + + if existing_tukar_guling: + raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) picking = self.operations if picking.picking_type_id.id == 30 and self.return_type == 'tukar_guling': raise UserError("❌ BU/PICK tidak boleh di retur tukar guling") @@ -530,7 +530,7 @@ class TukarGuling(models.Model): raise UserError( _("Qty di Koli tidak sesuai dengan qty retur untuk produk %s") % line.product_id.display_name ) - # self._check_invoice_on_revisi_so() + # self._check_invoice_on_retur_so() self._validate_product_lines() if self.state != 'draft': @@ -553,7 +553,7 @@ class TukarGuling(models.Model): self.state = 'done' # OUT revisi SO - elif self.operations.picking_type_id.id == 29 and self.return_type == 'revisi_so': + elif self.operations.picking_type_id.id == 29 and self.return_type == 'retur_so': total_ort = self.env['stock.picking'].search_count([ ('tukar_guling_id', '=', self.id), ('picking_type_id', '=', 74), @@ -567,7 +567,7 @@ class TukarGuling(models.Model): self.state = 'done' # PICK revisi SO - elif self.operations.picking_type_id.id == 30 and self.return_type == 'revisi_so': + elif self.operations.picking_type_id.id == 30 and self.return_type == 'retur_so': done_ort = self.env['stock.picking'].search([ ('tukar_guling_id', '=', self.id), ('picking_type_id', '=', 74), @@ -581,7 +581,7 @@ class TukarGuling(models.Model): def action_approve(self): self.ensure_one() self._validate_product_lines() - # self._check_invoice_on_revisi_so() + # self._check_invoice_on_retur_so() self._check_not_allow_tukar_guling_on_bu_pick() operasi = self.operations.picking_type_id.id @@ -631,7 +631,7 @@ class TukarGuling(models.Model): elif rec.state == 'approval_finance': if not rec.env.user.has_group('indoteknik_custom.group_role_fat'): raise UserError("Hanya Finance Manager yang boleh approve tahap ini.") - # rec._check_invoice_on_revisi_so() + # rec._check_invoice_on_retur_so() rec.set_opt() rec.state = 'approval_logistic' rec.date_finance = now diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index f2f37606..f61b3828 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -38,9 +38,9 @@ class TukarGulingPO(models.Model): ) ba_num = fields.Char('Nomor BA', tracking=3) return_type = fields.Selection([ - ('revisi_po', 'Revisi PO'), + ('retur_po', 'Retur PO'), ('tukar_guling', 'Tukar Guling'), - ], string='Return Type', required=True, tracking=3) + ], string='Return Type', required=True, tracking=3, help='Retur PO (VRT-PRT),\n Tukar Guling (VRT-PRT-INPUT-PUT') notes = fields.Text('Notes', tracking=3) tukar_guling_po_id = fields.Many2one('tukar.guling.po', string='Tukar Guling PO', ondelete='cascade') line_ids = fields.One2many('tukar.guling.line.po', 'tukar_guling_po_id', string='Product Lines', tracking=3) @@ -143,9 +143,9 @@ class TukarGulingPO(models.Model): return res - # def _check_bill_on_revisi_po(self): + # def _check_bill_on_retur_po(self): # for record in self: - # if record.return_type == 'revisi_po' and record.origin: + # if record.return_type == 'retur_po' and record.origin: # bills = self.env['account.move'].search([ # ('invoice_origin', 'ilike', record.origin), # ('move_type', '=', 'in_invoice'), # hanya vendor bill @@ -153,7 +153,7 @@ class TukarGulingPO(models.Model): # ]) # if bills: # raise ValidationError( - # _("Tidak bisa memilih Return Type 'Revisi PO' karena PO %s sudah dibuat vendor bill. Harus Cancel Jika ingin melanjutkan") % record.origin + # _("Tidak bisa memilih Return Type 'Retur PO' karena PO %s sudah dibuat vendor bill. Harus Cancel Jika ingin melanjutkan") % record.origin # ) @api.onchange('operations') @@ -284,7 +284,7 @@ class TukarGulingPO(models.Model): @api.constrains('return_type', 'operations') def _check_required_bu_fields(self): for record in self: - if record.return_type in ['revisi_po', 'tukar_guling'] and not record.operations: + if record.return_type in ['retur_po', 'tukar_guling'] and not record.operations: raise ValidationError("Operations harus diisi") @api.constrains('line_ids', 'state') @@ -350,7 +350,7 @@ class TukarGulingPO(models.Model): def write(self, vals): if self.operations.picking_type_id.id not in [75, 28]: raise UserError("❌ Tidak bisa retur bukan BU/INPUT atau BU/PUT!") - # self._check_bill_on_revisi_po() + # self._check_bill_on_retur_po() tipe = vals.get('return_type', self.return_type) if self.operations and self.operations.picking_type_id.id == 28 and tipe == 'tukar_guling': @@ -418,7 +418,7 @@ class TukarGulingPO(models.Model): def action_submit(self): self.ensure_one() - # self._check_bill_on_revisi_po() + # self._check_bill_on_retur_po() self._validate_product_lines() self._check_not_allow_tukar_guling_on_bu_input() @@ -463,7 +463,7 @@ class TukarGulingPO(models.Model): def action_approve(self): self.ensure_one() self._validate_product_lines() - # self._check_bill_on_revisi_po() + # self._check_bill_on_retur_po() self._check_not_allow_tukar_guling_on_bu_input() if not self.operations: @@ -485,7 +485,7 @@ class TukarGulingPO(models.Model): elif rec.state == 'approval_finance': if not rec.env.user.has_group('indoteknik_custom.group_role_fat'): raise UserError("Hanya Finance yang boleh approve tahap ini.") - # rec._check_bill_on_revisi_po() + # rec._check_bill_on_retur_po() rec.set_opt() rec.state = 'approval_logistic' rec.date_finance = now @@ -501,7 +501,7 @@ class TukarGulingPO(models.Model): def update_doc_state(self): # bu input rev po - if self.operations.picking_type_id.id == 28 and self.return_type == 'revisi_po': + if self.operations.picking_type_id.id == 28 and self.return_type == 'retur_po': prt = self.env['stock.picking'].search([ ('tukar_guling_po_id', '=', self.id), ('state', '=', 'done'), @@ -510,7 +510,7 @@ class TukarGulingPO(models.Model): if self.state == 'approved' and prt: self.state = 'done' # bu put rev po - elif self.operations.picking_type_id.id == 75 and self.return_type == 'revisi_po': + elif self.operations.picking_type_id.id == 75 and self.return_type == 'retur_po': total_prt = self.env['stock.picking'].search_count([ ('tukar_guling_po_id', '=', self.id), ('picking_type_id.id', '=', 76) diff --git a/indoteknik_custom/report/purchase_report.xml b/indoteknik_custom/report/purchase_report.xml index 6666235a..dd19340d 100644 --- a/indoteknik_custom/report/purchase_report.xml +++ b/indoteknik_custom/report/purchase_report.xml @@ -60,10 +60,11 @@ <tr> <td style="width:50%; border:1px solid #ccc; border-radius:8px; padding:10px; background:#fcfcfc; vertical-align:top;"> <strong style="color:#d32f2f;">Alamat Pengiriman</strong><br/> - PT Indoteknik (Bandengan 1 Depan)<br/> - Jl. Bandengan Utara Komp A 8 B<br/> - RT. Penjaringan, Kec. Penjaringan, Jakarta (BELAKANG INDOMARET)<br/> - JK 14440 - Indonesia + PT. Indoteknik Dotcom Gemilang<br/> + Jl. Bandengan Utara Komp A 8 B + RT. Penjaringan, Kec. Penjaringan, Jakarta + (BELAKANG INDOMARET)<br/> + Daerah Khusus Ibukota Jakarta 14440 </td> <td style="width:50%; border:1px solid #ccc; border-radius:8px; padding:10px; background:#fcfcfc; vertical-align:top;"> <strong style="color:#d32f2f;">Nama Vendor</strong><br/> @@ -93,12 +94,6 @@ <!-- NO & DESCRIPTION + IMAGE --> <td style="border:1px solid #ccc; padding: 6px; display:flex; align-items:center; gap:10px;"> - <t t-if="not line.image_small"> - <div style="width:40px; height:40px; background:#f5f5f5; border:1px solid #ddd; border-radius:6px; display:flex; align-items:center; justify-content:center; color:#999; font-size:10px;"> - N/A - </div> - </t> - <!-- TEKS --> <div style="display:flex; flex-direction:column; flex:1;"> <span style="font-weight:bold; margin-bottom:2px;"> @@ -107,12 +102,12 @@ </div> </td> - <t t-if="line.image_small"> <td style="border:1px solid #ccc; padding:6px; text-align:center;"> - <img t-att-src="image_data_uri(line.image_small)" - style="width:100px; height:100px; object-fit:contain; border:1px solid #ddd; border-radius:6px; background:#fff;"/> - </td> + <t t-if="line.image_small"> + <img t-att-src="image_data_uri(line.image_small)" + style="width:100px; height:100px; object-fit:contain; border:1px solid #ddd; border-radius:6px; background:#fff;"/> </t> + </td> <!-- QTY --> <td style="border:1px solid #ccc; padding:6px; text-align:center;"> <span t-field="line.product_qty"/> <span t-field="line.product_uom"/> @@ -135,9 +130,9 @@ </tr> <!-- WEBSITE DESCRIPTION --> - <t t-if="line.product_id.website_description"> + <t t-if="line.show_description"> <tr t-attf-style="background-color: #{ '#fef5f5' if line_index % 2 == 0 else '#fffafa' }; "> - <td colspan="6" style="padding: 10px 14px; font-size:12px; line-height:1.3; font-style:italic; color:#555; border-left:1px solid #ccc; border-right:1px solid #ccc; border-bottom:1px solid #ccc;"> + <td colspan="6" style="padding: 10px 14px; font-size:10px; line-height:1.3; font-style:italic; color:#555; border-left:1px solid #ccc; border-right:1px solid #ccc; border-bottom:1px solid #ccc;"> <div t-raw="line.product_id.website_description"/> </td> </tr> diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 25273893..ea6670eb 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -183,8 +183,11 @@ access_production_purchase_match,access.production.purchase.match,model_producti access_image_carousel,access.image.carousel,model_image_carousel,,1,1,1,1 access_v_sale_notin_matchpo,access.v.sale.notin.matchpo,model_v_sale_notin_matchpo,,1,1,1,1 access_approval_payment_term,access.approval.payment.term,model_approval_payment_term,,1,1,1,1 +access_purchase_order_update_date_wizard,access.purchase.order.update.date.wizard,model_purchase_order_update_date_wizard,,1,1,1,1 +access_change_date_planned_wizard,access.change.date.planned.wizard,model_change_date_planned_wizard,,1,1,1,1 access_refund_sale_order,access.refund.sale.order,model_refund_sale_order,base.group_user,1,1,1,1 access_refund_sale_order_line,access.refund.sale.order.line,model_refund_sale_order_line,base.group_user,1,1,1,1 + access_purchasing_job_seen,purchasing.job.seen,model_purchasing_job_seen,,1,1,1,1 access_tukar_guling_all_users,tukar.guling.all.users,model_tukar_guling,base.group_user,1,1,1,1 @@ -192,10 +195,10 @@ access_tukar_guling_line_all_users,tukar.guling.line.all.users,model_tukar_gulin access_tukar_guling_po_all_users,tukar.guling.po.all.users,model_tukar_guling_po,base.group_user,1,1,1,1 access_tukar_guling_line_po_all_users,tukar.guling.line.po.all.users,model_tukar_guling_line_po,base.group_user,1,1,1,1 access_tukar_guling_mapping_koli_all_users,tukar.guling.mapping.koli.all.users,model_tukar_guling_mapping_koli,base.group_user,1,1,1,1 -access_purchase_order_update_date_wizard,access.purchase.order.update.date.wizard,model_purchase_order_update_date_wizard,base.group_user,1,1,1,1 access_sync_promise_date_wizard,access.sync.promise.date.wizard,model_sync_promise_date_wizard,base.group_user,1,1,1,1 access_sync_promise_date_wizard_line,access.sync.promise.date.wizard.line,model_sync_promise_date_wizard_line,base.group_user,1,1,1,1 access_change_date_planned_wizard,access.change.date.planned.wizard,model_change_date_planned_wizard,,1,1,1,1 access_unpaid_invoice_view,access.unpaid.invoice.view,model_unpaid_invoice_view,base.group_user,1,1,1,1 access_surat_piutang_user,surat.piutang user,model_surat_piutang,base.group_user,1,1,1,1 -access_surat_piutang_line_user,surat.piutang.line user,model_surat_piutang_line,base.group_user,1,1,1,1
\ No newline at end of file +access_surat_piutang_line_user,surat.piutang.line user,model_surat_piutang_line,base.group_user,1,1,1,1 +access_sj_tele,access.sj.tele,model_sj_tele,base.group_system,1,1,1,1 diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml index 821f3295..7feec934 100755 --- a/indoteknik_custom/views/purchase_order.xml +++ b/indoteknik_custom/views/purchase_order.xml @@ -99,6 +99,7 @@ <field name="total_delivery_amt" attrs="{'required': [('partner_id', 'in', [9688, 29712])]}"/> <field name="product_bom_id" attrs="{'invisible': [('product_bom_id', '=', None)]}"/> <field name="manufacturing_id" attrs="{'invisible': [('product_bom_id', '=', None)]}"/> + <field name="show_description" attrs="{'invisible': [('partner_id', 'not in', [5571, 35475, 38198, 88261,37905, 9688, 20625, 1371, 37902, 12119])]}" widget="boolean_toggle"/> <!-- <field name="move_id" domain="[('move_type','=','entry')]" context="{'form_view_ref': 'account.view_move_form'}" options="{'no_create': True}"/> --> </field> <field name="amount_total" position="after"> @@ -112,6 +113,7 @@ </field> <field name="product_id" position="before"> <field name="line_no" attrs="{'readonly': 1}" optional="hide"/> + <field name="show_description" optional="hide" widget="boolean_toggle"/> </field> <field name="product_id" position="attributes"> <attribute name="options">{'no_create': True}</attribute> @@ -181,19 +183,20 @@ </field> <field name="order_line" position="attributes"> - <attribute name="attrs">{'readonly': ['|', ('state', 'in', ['purchase', 'done', 'cancel']), ('has_active_invoice', '=', True)]}</attribute> + <!-- <attribute name="attrs">{'readonly': ['|', ('state', 'in', ['purchase', 'done', 'cancel']), ('has_active_invoice', '=', True)]}</attribute> --> + <attribute name="attrs">{'readonly': [('has_active_invoice', '=', True)]}</attribute> </field> <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='price_unit']" position="attributes"> - <attribute name="attrs">{'readonly': [], 'required': True}</attribute> + <attribute name="attrs">{'readonly': [('state', 'in', ['purchase', 'done', 'cancel'])], 'required': True}</attribute> </xpath> <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='taxes_id']" position="attributes"> - <attribute name="attrs">{'readonly': []}</attribute> + <attribute name="attrs">{'readonly': [('state', 'in', ['purchase', 'done', 'cancel'])]}</attribute> </xpath> <xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='product_qty']" position="attributes"> - <attribute name="attrs">{'required': True}</attribute> + <attribute name="attrs">{'readonly': [('state', 'in', ['purchase', 'done', 'cancel'])], 'required': True}</attribute> </xpath> <xpath expr="//form/sheet/notebook/page[@name='purchase_delivery_invoice']" position="before"> diff --git a/indoteknik_custom/views/refund_sale_order.xml b/indoteknik_custom/views/refund_sale_order.xml index 0c6cd371..51f3deab 100644 --- a/indoteknik_custom/views/refund_sale_order.xml +++ b/indoteknik_custom/views/refund_sale_order.xml @@ -92,7 +92,7 @@ </div> <field name="show_approval_alert" invisible="1"/> <div class="alert alert-info" role="alert" - attrs="{'invisible': ['|', ('show_approval_alert', '=', False), ('status', 'in', ['reject', 'refund'])]}"> + attrs="{'invisible': ['|', ('show_approval_alert', '=', False), '|', ('status', 'in', ['reject', 'refund']), ('refund_type', 'not in', ['retur_full', 'retur_sebagian'])]}"> ⚠️ SO sudah melakukan retur barang. Silakan lanjutkan refund. </div> </xpath> diff --git a/indoteknik_custom/views/report_logbook_sj.xml b/indoteknik_custom/views/report_logbook_sj.xml index 94f6c2ab..46260cd5 100644 --- a/indoteknik_custom/views/report_logbook_sj.xml +++ b/indoteknik_custom/views/report_logbook_sj.xml @@ -12,15 +12,18 @@ <field name="date_approve"/> <field name="approve_by_finance"/> <field name="state"/> + </tree> </field> - </record> + </record> <record id="report_logbook_sj_line_tree" model="ir.ui.view"> <field name="name">report.logbook.sj.line.tree</field> <field name="model">report.logbook.sj.line</field> <field name="arch" type="xml"> <tree editable="bottom"> +<!-- <field name="sequence" widget="handle"/>--> + <field name="line_num" string="No" readonly="1"/> <field name="name"/> <field name="driver_id"/> <field name="departure_date"/> @@ -42,50 +45,47 @@ <field name="arch" type="xml"> <form> <header> - <button name="approve" - string="Validate" - type="object" - /> + <button name="approve" string="Validate" type="object" /> </header> <sheet string="Report logbook SJ"> - <div class="oe_button_box" name="button_box"/> + <div class="oe_button_box" name="button_box"/> + <group> <group> - <group> - <field name="name" readonly="1"/> - <field name="date" readonly="1"/> - <field name="date_approve" readonly="1"/> - </group> - <group> - <field name="approve_by_finance" readonly="1"/> - <field name="state" readonly="1"/> - <field name="created_by" readonly="1"/> - <field name="approve_by" readonly="1"/> - <field name="count_line" readonly="1"/> - </group> + <field name="name" readonly="1"/> + <field name="date" readonly="1"/> + <field name="date_approve" readonly="1"/> </group> - <notebook> - <page string="Line"> - <field name="report_logbook_sj_line"/> - </page> - </notebook> - </sheet> - <div class="oe_chatter"> - <field name="message_follower_ids" widget="mail_followers"/> - <field name="message_ids" widget="mail_thread"/> - </div> + <group> + <field name="approve_by_finance" readonly="1"/> + <field name="state" readonly="1"/> + <field name="created_by" readonly="1"/> + <field name="approve_by" readonly="1"/> + <field name="count_line" readonly="1"/> + </group> + </group> + <notebook> + <page string="Line"> + <field name="report_logbook_sj_line"/> + </page> + </notebook> + </sheet> + <div class="oe_chatter"> + <field name="message_follower_ids" widget="mail_followers"/> + <field name="message_ids" widget="mail_thread"/> + </div> </form> </field> </record> <record id="report_logbook_sj_view_search" model="ir.ui.view"> - <field name="name">report.logbook.sj.search.view</field> <!-- Made the name more descriptive --> - <field name="model">report.logbook.sj</field> - <field name="arch" type="xml"> - <search string="Search Report"> - <field name="sj_number"/> - </search> - </field> - </record> + <field name="name">report.logbook.sj.search.view</field> <!-- Made the name more descriptive --> + <field name="model">report.logbook.sj</field> + <field name="arch" type="xml"> + <search string="Search Report"> + <field name="sj_number"/> + </search> + </field> + </record> <record id="report_logbook_sj_action" model="ir.actions.act_window"> <field name="name">Report Logbook SJ</field> @@ -94,9 +94,19 @@ <field name="view_mode">tree,form</field> </record> - <menuitem id="menu_report_logbook_sj" - name="Report Logbook SJ" - action="report_logbook_sj_action" - parent="account.menu_finance_reports" - sequence="200"/> + <menuitem id="menu_report_logbook_sj" name="Report Logbook SJ" action="report_logbook_sj_action" parent="account.menu_finance_reports" sequence="200"/> + + <data noupdate="1"> + <record id="cron_daily_logbook_gap_to_telegram" model="ir.cron"> + <field name="name">Daily Logbook SJ Gap → Telegram</field> + <field name="model_id" ref="model_report_logbook_sj"/> + <field name="state">code</field> + <field name="code">model.cron_daily_logbook_gap_to_telegram()</field> + <field name="interval_number">1</field> + <field name="interval_type">days</field> + <field name="numbercall">-1</field> + <field name="user_id" ref="base.user_root"/> + <field name="active">False</field> + </record> + </data> </odoo>
\ No newline at end of file diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 44da3e13..8d56bbbd 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -297,9 +297,9 @@ <field name="note" optional="hide"/> <field name="note_procurement" optional="hide"/> <field name="vendor_subtotal" optional="hide"/> - <field name="unreserved_percent" widget="percentpie" string="Unreserved"/> - <field name="reserved_percent" widget="percentpie" string="Reserved"/> - <field name="delivered_percent" widget="percentpie" string="Delivered"/> + <field name="unreserved_percent" widget="percentpie" string="Unreserved" optional="hide"/> + <field name="reserved_percent" widget="percentpie" string="Reserved" optional="hide"/> + <field name="delivered_percent" widget="percentpie" string="Delivered" optional="hide"/> <field name="weight" optional="hide"/> <field name="is_has_disc" string="Flash Sale Item?" readonly="1" optional="hide"/> <field name="amount_voucher_disc" string="Voucher" readonly="1" optional="hide"/> diff --git a/indoteknik_custom/views/sj_tele.xml b/indoteknik_custom/views/sj_tele.xml new file mode 100644 index 00000000..cefcc968 --- /dev/null +++ b/indoteknik_custom/views/sj_tele.xml @@ -0,0 +1,15 @@ +<odoo> + <data noupdate="1"> + <record id="woi" model="ir.cron"> + <field name="name">SJ TELE</field> + <field name="model_id" ref="model_sj_tele"/> + <field name="state">code</field> + <field name="code">model.woi()</field> + <field name="interval_number">1</field> + <field name="interval_type">days</field> + <field name="numbercall">-1</field> + <field name="user_id" ref="base.user_root"/> + <field name="active">False</field> + </record> + </data> +</odoo>
\ No newline at end of file diff --git a/indoteknik_custom/views/tukar_guling_po.xml b/indoteknik_custom/views/tukar_guling_po.xml index 548a209f..4a9ab25d 100644 --- a/indoteknik_custom/views/tukar_guling_po.xml +++ b/indoteknik_custom/views/tukar_guling_po.xml @@ -87,7 +87,7 @@ <!-- <field name="srt_num" readonly="1"/>--> <field name="operations" string="Operations" attrs="{ - 'required': [('return_type', 'in', ['revisi_po', 'tukar_guling'])] + 'required': [('return_type', 'in', ['retur_po', 'tukar_guling'])] }"/> <!-- <field name="origin" readonly="1"/>--> <field name="origin_po" readonly="1"/> |
