diff options
| author | Miqdad <ahmadmiqdad27@gmail.com> | 2025-06-22 19:14:06 +0700 |
|---|---|---|
| committer | Miqdad <ahmadmiqdad27@gmail.com> | 2025-06-22 19:14:06 +0700 |
| commit | 4430c79a3388c591725cdbd12e32c6c371b6ecd3 (patch) | |
| tree | a5935e7e5fda73c7929d4ef5a84e734b9c305626 | |
| parent | 680748ae128a90b9999acff60c770e2472c7fcbe (diff) | |
| parent | eeb72c4ed24c33403bb733a51198b9cc0f356e6a (diff) | |
Merge branch 'odoo-backup' of https://bitbucket.org/altafixco/indoteknik-addons into tukar_guling
| -rw-r--r-- | indoteknik_api/controllers/api_v1/partner.py | 2 | ||||
| -rw-r--r-- | indoteknik_api/controllers/api_v1/state.py | 10 | ||||
| -rw-r--r-- | indoteknik_custom/models/account_move.py | 40 | ||||
| -rw-r--r-- | indoteknik_custom/models/commision.py | 14 | ||||
| -rw-r--r-- | indoteknik_custom/models/invoice_reklas.py | 10 | ||||
| -rwxr-xr-x | indoteknik_custom/models/purchase_order.py | 75 | ||||
| -rwxr-xr-x | indoteknik_custom/models/sale_order.py | 57 | ||||
| -rw-r--r-- | indoteknik_custom/models/sale_orders_multi_update.py | 7 | ||||
| -rw-r--r-- | indoteknik_custom/models/user_pengajuan_tempo_request.py | 6 | ||||
| -rw-r--r-- | indoteknik_custom/views/account_move.xml | 6 | ||||
| -rwxr-xr-x | indoteknik_custom/views/purchase_order.xml | 9 | ||||
| -rwxr-xr-x | indoteknik_custom/views/sale_order.xml | 15 | ||||
| -rw-r--r-- | indoteknik_custom/views/stock_picking.xml | 1 |
13 files changed, 218 insertions, 34 deletions
diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 37ae8d5f..b8bd21be 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -322,7 +322,7 @@ class Partner(controller.Controller): data = True if pengajuan_tempo.id else False return self.response(data) - @http.route(prefix + 'partner/pengajuan_tempo', auth='public', methods=['POST'], csrf=False) + @http.route(prefix + 'partner/pengajuan_tempo', auth='public', methods=['POST', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() def write_pengajuan_tempo(self, **kw): try: diff --git a/indoteknik_api/controllers/api_v1/state.py b/indoteknik_api/controllers/api_v1/state.py index 958359a7..c03042e7 100644 --- a/indoteknik_api/controllers/api_v1/state.py +++ b/indoteknik_api/controllers/api_v1/state.py @@ -8,12 +8,8 @@ class District(controller.Controller): @http.route(prefix + 'state', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_state(self, **kw): - tempo = kw.get('tempo') - parameters = [] - if tempo == 'true': - parameters.append(('country_id', '=', 100)) + parameters = [('country_id', '=', 100)] # selalu ambil country_id = 100 - name = kw.get('name') if name: name = '%' + name.replace(' ', '%') + '%' @@ -22,7 +18,7 @@ class District(controller.Controller): states = request.env['res.country.state'].search(parameters) data = [] for state in states: - data.append({ 'id': state.id, 'name': state.name}) - + data.append({'id': state.id, 'name': state.name}) + return self.response(data) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 54eaabcf..66020a69 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -87,6 +87,17 @@ class AccountMove(models.Model): # result.append((move.id, move.display_name)) # return result + @api.onchange('invoice_date') + def _onchange_invoice_date(self): + if self.invoice_date: + self.date = self.invoice_date + + @api.onchange('date') + def _onchange_date(self): + if self.date: + self.invoice_date = self.date + + def compute_length_of_payment(self): for rec in self: payment_term = rec.invoice_payment_term_id.line_ids[0].days @@ -145,13 +156,38 @@ class AccountMove(models.Model): } template.send_mail(record.id, email_values=email_values, force_send=True) + # @api.model + # def create(self, vals): + # vals['nomor_kwitansi'] = self.env['ir.sequence'].next_by_code('nomor.kwitansi') or '0' + # result = super(AccountMove, self).create(vals) + # # result._update_line_name_from_ref() + # return result + @api.model def create(self, vals): - vals['nomor_kwitansi'] = self.env['ir.sequence'].next_by_code('nomor.kwitansi') or '0' + vals['nomor_kwitansi'] = self.env['ir.sequence'].next_by_code('nomor.kwitansi') or '0' result = super(AccountMove, self).create(vals) - # result._update_line_name_from_ref() + + # Tambahan: jika ini Vendor Bill dan tanggal belum diisi + if result.move_type == 'in_invoice' and not vals.get('invoice_date') and not vals.get('date'): + po = result.purchase_order_id + if po: + # Cari receipt dari PO + picking = self.env['stock.picking'].search([ + ('purchase_id', '=', po.id), + ('picking_type_code', '=', 'incoming'), + ('state', '=', 'done'), + ('date_done', '!=', False), + ], order='date_done desc', limit=1) + + if picking: + receipt_date = picking.date_done + result.invoice_date = receipt_date + result.date = receipt_date + return result + def compute_so_shipping_paid_by(self): for record in self: record.so_shipping_paid_by = record.sale_id.shipping_paid_by diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index d3392a0c..03d32d2d 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -177,8 +177,9 @@ class CustomerCommision(models.Model): ], string='Status') commision_percent = fields.Float(string='Commision %', tracking=3) commision_amt = fields.Float(string='Commision Amount', tracking=3) - cashback = fields.Float(string='Cashback', tracking=3) - total_commision = fields.Float(string='Total Commision', tracking=3) + cashback = fields.Float(string='Cashback', compute="compute_cashback") + total_commision = fields.Float(string='Total Commision', compute="compute_cashback") + total_cashback = fields.Float(string='Total Cashback') commision_amt_text = fields.Char(string='Commision Amount Text', compute='compute_delivery_amt_text') total_dpp = fields.Float(string='Total DPP', compute='_compute_total_dpp') commision_type = fields.Selection([ @@ -318,9 +319,6 @@ class CustomerCommision(models.Model): if self.commision_amt == 0: self.commision_amt = self.commision_percent * self.total_dpp // 100 - if self.commision_type == 'cashback': - self.cashback = self.commision_amt * 0.15 - self.total_commision = self.commision_amt * 0.85 @api.constrains('commision_amt') def _onchange_commision_amt(self): @@ -333,11 +331,13 @@ class CustomerCommision(models.Model): if self.total_dpp > 0 and self.commision_percent == 0: self.commision_percent = (self.commision_amt / self.total_dpp) * 100 - @api.constrains('commision_type') - def _onchange_commision_amt(self): + def compute_cashback(self): if self.commision_type == 'cashback' and self.commision_amt > 0: self.cashback = self.commision_amt * 0.15 self.total_commision = self.commision_amt * 0.85 + else: + self.cashback = 0 + self.total_commision = 0 def _compute_total_dpp(self): for data in self: diff --git a/indoteknik_custom/models/invoice_reklas.py b/indoteknik_custom/models/invoice_reklas.py index 5145e098..4ed7f0bf 100644 --- a/indoteknik_custom/models/invoice_reklas.py +++ b/indoteknik_custom/models/invoice_reklas.py @@ -87,10 +87,10 @@ class InvoiceReklas(models.TransientModel): po_name = invoice.purchase_order_id.name if invoice.purchase_order_id else '' # Susun nama referensi dengan aman - ref_name = 'REKLAS {} UANG MUKA {} {}{} {}'.format( + ref_name = 'REKLAS {} UANG MUKA {}{}{} {}'.format( self.reklas_id.name or '', 'PENJUALAN' if self.reklas_type == 'penjualan' else 'PEMBELIAN', - invoice.name or '', + f" {invoice.name}" if invoice.name != self.reklas_id.name else '', f" - {po_name}" if po_name else '', invoice.partner_id.name or '' ) @@ -102,11 +102,15 @@ class InvoiceReklas(models.TransientModel): 'journal_id': 13 } + if invoice.purchase_order_id: + parameters_header['purchase_order_id'] = invoice.purchase_order_id.id + account_move = request.env['account.move'].create([parameters_header]) _logger.info('Success Reklas with %s' % account_move.name) # â
Set Bill asal sebagai source document - account_move.bill_id = invoice.id + if invoice.move_type == 'in_invoice': + account_move.bill_id = invoice.id # Tambahkan info asal invoice ke jurnal (opsional) account_move.invoice_origin = invoice.name diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 004a1fa4..1a7e50f8 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -91,6 +91,63 @@ class PurchaseOrder(models.Model): is_cab_visible = fields.Boolean(string='Tampilkan Tombol CAB', compute='_compute_is_cab_visible') + # picking_ids = fields.One2many('stock.picking', 'purchase_id', string='Pickings') + + bu_related_count = fields.Integer( + string="BU Related Count", + compute='_compute_bu_related_count' + ) + + @api.depends('name') + def _compute_bu_related_count(self): + for order in self: + if not order.name: + order.bu_related_count = 0 + continue + + # BU langsung dari PO + base_bu = self.env['stock.picking'].search([ + ('name', 'ilike', 'BU/'), + ('origin', 'ilike', order.name) + ]) + base_names = base_bu.mapped('name') + + # Return dari BU di atas + return_bu = self.env['stock.picking'].search([ + ('origin', 'in', [f"Return of {name}" for name in base_names]) + ]) + + order.bu_related_count = len(base_bu) + len(return_bu) + + def action_view_related_bu(self): + self.ensure_one() + + # Step 1: cari semua BU pertama (PUT, INT) yang berasal dari PO ini + base_bu = self.env['stock.picking'].search([ + ('name', 'ilike', 'BU/'), + ('origin', 'ilike', self.name) + ]) + base_bu_names = base_bu.mapped('name') + + # Step 2: cari BU turunan (seperti BU/VRT) yang origin-nya mengandung nama BU tersebut + domain = [ + '|', + '&', + ('name', 'ilike', 'BU/'), + ('origin', 'ilike', self.name), + ('origin', 'in', [f"Return of {name}" for name in base_bu_names]) + ] + + return { + 'name': 'Related BU (INT/PRT/PUT/VRT)', + 'type': 'ir.actions.act_window', + 'res_model': 'stock.picking', + 'view_mode': 'tree,form', + 'target': 'current', + 'domain': domain, + } + + @api.depends('move_id.state') def _compute_is_cab_visible(self): for order in self: @@ -452,6 +509,18 @@ class PurchaseOrder(models.Model): 'company_id': self.company_id.id, 'payment_schedule': payment_schedule } + + receipt = self.env['stock.picking'].search([ + ('purchase_id', '=', self.id), + ('state', '=', 'done'), + ('picking_type_code', '=', 'incoming'), + ('date_done', '!=', False) + ], order='date_done desc', limit=1) + + if receipt: + invoice_vals['invoice_date'] = receipt.date_done + invoice_vals['date'] = receipt.date_done + return invoice_vals def _compute_matches_so(self): @@ -917,6 +986,12 @@ class PurchaseOrder(models.Model): if self.product_bom_id: self._remove_product_bom() + # Tambahan: redirect ke BU hanya untuk single PO yang berhasil dikonfirmasi + _logger.info("Jumlah PO: %s | State: %s", len(self), self.state) + if len(self) == 1: + _logger.info("Redirecting ke BU") + return self.action_view_related_bu() + return res def _remove_product_bom(self): diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 68beffbc..c8d4a712 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -349,6 +349,47 @@ class SaleOrder(models.Model): date_unhold = fields.Datetime(string='Date Unhold', tracking=True, readonly=True, help='Waktu ketika SO di Unhold' ) + et_products = fields.Datetime(string='ET Products', compute='_compute_et_products', help="Leadtime produk berdasarkan SLA vendor, tanpa logistik.") + + eta_date_reserved = fields.Datetime( + string="Date Reserved", + compute="_compute_eta_date_reserved", + help="Tanggal pertama kali barang berhasil di-reservasi pada DO (BU/PICK/) yang berstatus Siap Dikirim." + ) + + @api.depends('order_line.product_id', 'date_order') + def _compute_et_products(self): + jakarta = pytz.timezone("Asia/Jakarta") + for order in self: + if not order.order_line or not order.date_order: + order.et_products = False + continue + + # Ambil tanggal order sebagai basis + base_date = order.date_order + if base_date.tzinfo is None: + base_date = jakarta.localize(base_date) + else: + base_date = base_date.astimezone(jakarta) + + # Ambil nilai SLA vendor dalam hari + sla_data = order.calculate_sla_by_vendor(order.order_line) + sla_days = sla_data.get('slatime', 1) + + # Hitung ETA produk (tanpa logistik) + eta_datetime = base_date + timedelta(days=sla_days) + + # Simpan ke field sebagai UTC-naive datetime (standar Odoo) + order.et_products = eta_datetime.astimezone(pytz.utc).replace(tzinfo=None) + + @api.depends('picking_ids.state', 'picking_ids.date_reserved') + def _compute_eta_date_reserved(self): + for order in self: + pickings = order.picking_ids.filtered( + lambda p: p.state == 'assigned' and p.date_reserved and 'BU/PICK/' in (p.name or '') + ) + order.eta_date_reserved = min(pickings.mapped('date_reserved')) if pickings else False + @api.onchange('shipping_cost_covered') def _onchange_shipping_cost_covered(self): if self.shipping_cost_covered == 'indoteknik' and self.select_shipping_option == 'biteship': @@ -486,7 +527,7 @@ class SaleOrder(models.Model): ('sale_order_id', '=', sale_order_id), ('provider', 'ilike', provider), ]) - if matched == 0: + if self.select_shipping_option == 'biteship' and matched == 0: self.carrier_id = self._origin.carrier_id self.shipping_option_id = self._origin.shipping_option_id or False return { @@ -550,11 +591,11 @@ class SaleOrder(models.Model): if shipping_option.exists(): courier_service = shipping_option.courier_service_code vals['delivery_service_type'] = courier_service - _logger.info("đ°ī¸ Set delivery_service_type: %s from shipping_option_id: %s", courier_service, shipping_option_id) + _logger.info("Set delivery_service_type: %s from shipping_option_id: %s", courier_service, shipping_option_id) else: - _logger.warning("â ī¸ shipping_option_id %s not found or invalid.", shipping_option_id) + _logger.warning("shipping_option_id %s not found or invalid.", shipping_option_id) else: - _logger.info("âšī¸ shipping_option_id not found in vals or record.") + _logger.info("shipping_option_id not found in vals or record.") # @api.model # def fields_get(self, allfields=None, attributes=None): @@ -1071,7 +1112,13 @@ class SaleOrder(models.Model): message_lines.append("<br/>") origin_address = "Jl. Bandengan Utara Komp A & BRT. Penjaringan, Kec. Penjaringan, Jakarta (BELAKANG INDOMARET) KOTA JAKARTA UTARA PENJARINGAN" - destination_address = shipping_address.alamat_lengkap_text or shipping_address.street or shipping_address.name or '' + destination_address = ', '.join(filter(None, [ + shipping_address.street, + shipping_address.kelurahan_id.name if shipping_address.kelurahan_id else None, + shipping_address.kecamatan_id.name if shipping_address.kecamatan_id else None, + shipping_address.kota_id.name if shipping_address.kota_id else None, + shipping_address.state_id.name if shipping_address.state_id else None + ])) if use_coordinate: origin_suffix = f"(Koordinat: {origin_data.get('origin_latitude')}, {origin_data.get('origin_longitude')})" destination_suffix = f"(Koordinat: {destination_data.get('destination_latitude')}, {destination_data.get('destination_longitude')})" diff --git a/indoteknik_custom/models/sale_orders_multi_update.py b/indoteknik_custom/models/sale_orders_multi_update.py index 95cfde21..962f60b5 100644 --- a/indoteknik_custom/models/sale_orders_multi_update.py +++ b/indoteknik_custom/models/sale_orders_multi_update.py @@ -11,6 +11,13 @@ class SaleOrdersMultiUpdate(models.TransientModel): sale_ids = self._context['sale_ids'] sales = self.env['sale.order'].browse(sale_ids) sales.action_multi_update_invoice_status() + + for sale in sales: + sale.message_post( + body="Sales Order has been marked as Completed", + message_type="comment" + ) + return { 'type': 'ir.actions.client', 'tag': 'display_notification', diff --git a/indoteknik_custom/models/user_pengajuan_tempo_request.py b/indoteknik_custom/models/user_pengajuan_tempo_request.py index 87227764..600381c0 100644 --- a/indoteknik_custom/models/user_pengajuan_tempo_request.py +++ b/indoteknik_custom/models/user_pengajuan_tempo_request.py @@ -371,7 +371,7 @@ class UserPengajuanTempoRequest(models.Model): @api.onchange('tempo_limit') def _onchange_tempo_limit(self): for tempo in self: - if tempo.env.user.id not in (7, 688, 28, 377, 12182, 375): + if tempo.env.user.id not in (7, 688, 28, 19, 375): raise UserError("Limit tempo hanya bisa diubah oleh Sales Manager atau Direktur") def button_approve(self): for tempo in self: @@ -381,7 +381,7 @@ class UserPengajuanTempoRequest(models.Model): if tempo.env.user.id in (688, 28, 7): raise UserError("Pengajuan tempo harus di approve oleh sales manager terlebih dahulu") else: - if tempo.env.user.id not in (377, 12182, 375): + if tempo.env.user.id not in (375, 19): # if tempo.env.user.id != 12182: raise UserError("Pengajuan tempo hanya bisa di approve oleh sales manager") else: @@ -400,7 +400,7 @@ class UserPengajuanTempoRequest(models.Model): if tempo.env.user.id == 7: raise UserError("Pengajuan tempo harus di approve oleh Finence terlebih dahulu") else: - if tempo.env.user.id not in (688, 28, 12182): + if tempo.env.user.id not in (688, 28): # if tempo.env.user.id not in (288,28,12182): raise UserError("Pengajuan tempo hanya bisa di approve oleh Finence") else: diff --git a/indoteknik_custom/views/account_move.xml b/indoteknik_custom/views/account_move.xml index 0c2f9a68..0fc62293 100644 --- a/indoteknik_custom/views/account_move.xml +++ b/indoteknik_custom/views/account_move.xml @@ -33,12 +33,12 @@ </field> <field name="invoice_date" position="after"> <field name="sale_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'out_invoice')]}"/> - <field name="purchase_order_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'in_invoice')]}"/> + <!-- <field name="purchase_order_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'in_invoice')]}"/> --> </field> <field name="ref" position="after"> <field name="sale_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'entry')]}"/> - <!-- <field name="purchase_order_id" context="{'form_view_ref': 'purchase.purchase_order_form'}" options="{'no_create': True}"/> --> - <field name="bill_id" readonly="1" attrs="{'invisible': ['|', ('move_type', '!=', 'entry'), ('purchase_order_id', '!=', False)]}"/> + <field name="purchase_order_id" context="{'form_view_ref': 'purchase.purchase_order_form'}" options="{'no_create': True}"/> + <field name="bill_id" readonly="1"/> </field> <field name="partner_shipping_id" position="before"> <field name="real_invoice_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'out_invoice')]}"/> diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml index 9084bcbb..7b568d07 100755 --- a/indoteknik_custom/views/purchase_order.xml +++ b/indoteknik_custom/views/purchase_order.xml @@ -27,6 +27,15 @@ <t t-esc="record.move_id.name"/> </span> </button> + <button type="object" + name="action_view_related_bu" + class="oe_stat_button" + icon="fa-truck" + style="width: 200px;" + attrs="{'invisible': [('state', 'in', ['draft', 'sent'])]}"> + <field name="bu_related_count" widget="statinfo" string="BU Related"/> + </button> + <field name="picking_count" invisible="1"/> </xpath> <button id="draft_confirm" position="after"> <button name="po_approve" diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index d0442677..2a159307 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -103,10 +103,10 @@ <field name="compute_fullfillment" invisible="1" /> </field> <field name="tag_ids" position="after"> - <field name="eta_date_start"/> + <!-- <field name="eta_date_start"/> --> <t t-esc="' to '"/> - <field name="eta_date" readonly="1"/> - <field name="expected_ready_to_ship"/> + <!-- <field name="eta_date" readonly="1"/> --> + <!-- <field name="expected_ready_to_ship"/> --> <field name="ready_to_ship_status_detail"/> <field name="flash_sale"/> <field name="margin_after_delivery_purchase"/> @@ -144,6 +144,15 @@ <field name="date_doc_kirim" readonly="1"/> <field name="notification" readonly="1"/> </field> + <xpath expr="//page[@name='other_information']/group/group[@name='sale_reporting']" position="after"> + <group string="ETA"> + <field name="et_products"/> + <field name="eta_date_reserved"/> + <field name="expected_ready_to_ship"/> + <field name="eta_date_start"/> + <field name="eta_date" readonly="1"/> + </group> + </xpath> <xpath expr="//form/sheet/notebook/page/field[@name='order_line']" position="attributes"> <attribute name="attrs"> diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 97a9fbed..c088e00c 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -17,6 +17,7 @@ <field name="driver_arrival_date" optional="hide"/> <field name="note_logistic" optional="hide"/> <field name="note" optional="hide"/> + <field name="sj_return_date" optional="hide"/> <field name="date_reserved" optional="hide"/> <field name="state_reserve" optional="hide"/> <field name="state_packing" widget="badge" decoration-success="state_packing == 'packing_done'" decoration-danger="state_packing == 'not_packing'" optional="hide"/> |
