diff options
| author | FIN-IT_AndriFP <it@fixcomart.co.id> | 2025-09-29 10:47:25 +0700 |
|---|---|---|
| committer | FIN-IT_AndriFP <it@fixcomart.co.id> | 2025-09-29 10:47:25 +0700 |
| commit | e1efb70e0002fec8ad684f7ab62f551f4c649963 (patch) | |
| tree | 67ddf1ea0cf8294758b552fde26cdcc25630d682 | |
| parent | 6582949e6e8f7017b8b44ed937d6042d8794fbc5 (diff) | |
| parent | ec21c4721f35028fc9b2a61bd0dbc6e4bf600e74 (diff) | |
Merge branch 'odoo-backup' of https://bitbucket.org/altafixco/indoteknik-addons into closing-apt
| -rw-r--r-- | indoteknik_api/controllers/api_v1/stock_picking.py | 58 | ||||
| -rw-r--r-- | indoteknik_custom/models/account_move_due_extension.py | 8 | ||||
| -rw-r--r-- | indoteknik_custom/models/purchasing_job.py | 19 | ||||
| -rw-r--r-- | indoteknik_custom/models/refund_sale_order.py | 133 | ||||
| -rwxr-xr-x | indoteknik_custom/models/sale_order.py | 17 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_picking.py | 5 | ||||
| -rw-r--r-- | indoteknik_custom/models/tukar_guling.py | 26 | ||||
| -rw-r--r-- | indoteknik_custom/report/purchase_report.xml | 2 | ||||
| -rw-r--r-- | indoteknik_custom/views/account_move_views.xml | 3 | ||||
| -rw-r--r-- | indoteknik_custom/views/stock_picking.xml | 1 |
10 files changed, 216 insertions, 56 deletions
diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 762e17c5..a4a9cf80 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -124,33 +124,53 @@ class StockPicking(controller.Controller): @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): - scanid = kw.get('scanid', '').strip() - sj_document = kw.get('sj_document', False) - paket_document = kw.get('paket_document', False) - - params = { - 'sj_documentation': sj_document, - 'paket_documentation': paket_document, - 'driver_arrival_date': datetime.utcnow(), + def write_partner_stock_picking_documentation(self, scanid, **kw): + sj_document = kw.get('sj_document', False) + paket_document = kw.get('paket_document', False) + dispatch_document = kw.get('dispatch_document', False) + + # ===== Role by EMAIL ===== + driver_emails = { + 'driverindoteknik@gmail.com', + 'sulistianaridwan8@gmail.com', } + dispatch_emails = { + 'rahmat.afiudin@gmail.com', + 'it@fixcomart.co.id' + } + + login = (request.env.user.login or '').lower() + is_dispatch_user = login in dispatch_emails + is_driver_user = (login in driver_emails) and not is_dispatch_user + + # ===== Validasi minimal ===== + if not sj_document or not paket_document: + return self.response(code=400, description='dispatch_document wajib untuk role dispatch login= %s' % login) + # if is_dispatch_user and not dispatch_document and not is_driver_user: + # return self.response(code=400, description='dispatch_document wajib untuk role dispatch login= %s' % login) + + # ===== Cari picking by id / picking_code ===== picking_data = False - if scanid.isdigit() and int(scanid) < 2147483647: - picking_data = request.env['stock.picking'].search([('id', '=', int(scanid))], limit=1) + if scanid.isdigit() and int(scanid) < 2147483646: + picking_data = request.env['stock.picking'].search([('id', '=', int(scanid))], limit=0) if not picking_data: - picking_data = request.env['stock.picking'].search([('picking_code', '=', scanid)], limit=1) + picking_data = request.env['stock.picking'].search([('picking_code', '=', scanid)], limit=0) if not picking_data: - return self.response(code=404, description='picking not found') - - picking_data.write(params) + return self.response(code=403, description='picking not found') - return self.response({ - 'name': picking_data.name - }) + params = { + 'sj_documentation': sj_document, + 'paket_documentation': paket_document, + 'driver_arrival_date': datetime.utcnow(), + } + if dispatch_document: + params['dispatch_documentation'] = dispatch_document + picking_data.write(params) + return self.response({'name': picking_data.name}) @http.route(prefix + 'webhook/biteship', type='json', auth='public', methods=['POST'], csrf=False) def update_status_from_biteship(self, **kw): @@ -160,7 +180,7 @@ class StockPicking(controller.Controller): # Karena type='json', Odoo secara otomatis akan mem-parsing JSON untuk Anda. # 'data' akan berisi dictionary Python dari payload JSON Biteship. data = request.jsonrequest - + # Log ini akan menunjukkan payload yang diterima (sudah dalam bentuk dict) _logger.info(f"Biteship Webhook: Parsed JSON data from request.jsonrequest: {json.dumps(data)}") diff --git a/indoteknik_custom/models/account_move_due_extension.py b/indoteknik_custom/models/account_move_due_extension.py index 40059bd9..55fc6c65 100644 --- a/indoteknik_custom/models/account_move_due_extension.py +++ b/indoteknik_custom/models/account_move_due_extension.py @@ -13,6 +13,7 @@ class DueExtension(models.Model): number = fields.Char(string='Document No', index=True, copy=False, readonly=True, tracking=True) partner_id = fields.Many2one('res.partner', string="Customer", readonly=True) + payment_term = fields.Char(string="Payment Term", readonly=True, compute='_compute_payment_term') order_id = fields.Many2one('sale.order', string="SO", readonly=True) amount_total = fields.Monetary( string="Amount Total SO", @@ -43,7 +44,12 @@ class DueExtension(models.Model): counter = fields.Integer(string="Counter", compute='_compute_counter') approve_by = fields.Many2one('res.users', string="Approve By", readonly=True) date_approve = fields.Datetime(string="Date Approve", readonly=True) - + + @api.depends('partner_id') + def _compute_payment_term(self): + for rec in self: + rec.payment_term = rec.partner_id.property_payment_term_id.name + @api.depends('order_id') def _compute_amount_total(self): for rec in self: diff --git a/indoteknik_custom/models/purchasing_job.py b/indoteknik_custom/models/purchasing_job.py index db733b5a..3151f0f6 100644 --- a/indoteknik_custom/models/purchasing_job.py +++ b/indoteknik_custom/models/purchasing_job.py @@ -29,20 +29,23 @@ class PurchasingJob(models.Model): so_number = fields.Text(string='SO Number', copy=False) check_pj = fields.Boolean(compute='_get_check_pj', string='Linked') + def action_open_job_detail(self): self.ensure_one() Seen = self.env['purchasing.job.seen'] + seen = Seen.search([ - ('user_id', '=', self.env.uid), ('product_id', '=', self.product_id.id) ], limit=1) if seen: - seen.so_snapshot = self.so_number - seen.seen_date = fields.Datetime.now() + seen.write({ + 'so_snapshot': self.so_number, + 'seen_date': fields.Datetime.now(), + 'user_id': self.env.user.id, }) else: Seen.create({ - 'user_id': self.env.uid, + 'user_id': self.env.user.id, 'product_id': self.product_id.id, 'so_snapshot': self.so_number, }) @@ -56,17 +59,13 @@ class PurchasingJob(models.Model): 'target': 'current', } - @api.depends('so_number') def _get_check_pj(self): + Seen = self.env['purchasing.job.seen'] for rec in self: - seen = self.env['purchasing.job.seen'].search([ - ('user_id', '=', self.env.uid), - ('product_id', '=', rec.product_id.id) - ], limit=1) + seen = Seen.search([('product_id', '=', rec.product_id.id)], limit=1) rec.check_pj = bool(seen and seen.so_snapshot == rec.so_number) - def unlink(self): # Example: Delete related records from the underlying model underlying_records = self.env['purchasing.job'].search([ diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py index 96082447..de9870f6 100644 --- a/indoteknik_custom/models/refund_sale_order.py +++ b/indoteknik_custom/models/refund_sale_order.py @@ -303,25 +303,60 @@ class RefundSaleOrder(models.Model): ('journal_id', '=', 7), ('state', '=', 'posted'), ]) + + misc = self.env['account.move'].search([ + ('ref', 'ilike', invoices.mapped('name')[0]), + ('ref', 'not ilike', 'reklas'), + ('journal_id', '=', 13), + ('state', '=', 'posted'), + ]) + moves2 = self.env['account.move'] + if so_ids: + so_names = self.env['sale.order'].browse(so_ids).mapped('name') + domain = [ + ('journal_id', '=', 11), + ('state', '=', 'posted'), + ('ref', 'ilike', 'dp') + ] + if so_names: + domain += ['|'] * (len(so_names) - 1) + for n in so_names: + domain.append(('ref', 'ilike', n)) + moves2 = self.env['account.move'].search(domain) has_moves = bool(moves) + has_moves2 = bool(moves2) has_piutangmdr = bool(piutangmdr) has_piutangbca = bool(piutangbca) + has_misc = bool(misc) ssos = self.env['sale.order'].browse(so_ids) has_settlement = any(so.payment_status == 'settlement' for so in ssos) sisa_uang_masuk = 0.0 + amounts = [] + 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')) + amounts.append(sum(moves.mapped('amount_total_signed'))) + amounts.append(sum(ssos.mapped('gross_amount'))) else: + if has_moves: + amounts.append(sum(moves.mapped('amount_total_signed'))) + if has_settlement: + amounts.append(sum(ssos.mapped('gross_amount'))) + + # sisanya bisa dijumlahkan tanpa konflik + if has_moves2: + amounts.append(sum(moves2.mapped('amount_total_signed'))) + if has_piutangbca: + amounts.append(sum(piutangbca.mapped('amount_total_signed'))) + if has_piutangmdr: + amounts.append(sum(piutangmdr.mapped('amount_total_signed'))) + if has_misc: + amounts.append(sum(misc.mapped('amount_total_signed'))) + + sisa_uang_masuk = sum(amounts) + + if not sisa_uang_masuk: raise UserError( "❌ Tidak bisa melakukan refund karena SO tidak memiliki Record Uang Masuk " "(Journal Uang Muka / Payment Invoices / Midtrans Payment)." @@ -529,7 +564,32 @@ class RefundSaleOrder(models.Model): ('state', '=', 'posted'), ]) - all_moves = moves | piutangbca | piutangmdr + moves2 = self.env['account.move'] + if rec.sale_order_ids: + so_names = rec.sale_order_ids.mapped('name') + + domain = [ + ('journal_id', '=', 11), + ('state', '=', 'posted'), + ('ref', 'ilike', 'dp') + ] + domain += ['|'] * (len(so_names) - 1) + for n in so_names: + domain.append(('ref', 'ilike', n)) + + moves2 = self.env['account.move'].search(domain) + + misc = self.env['account.move'] + if invoice_ids: + invoice_name = invoice_ids.mapped('name')[0] + misc = self.env['account.move'].search([ + ('ref', 'ilike', invoice_name), + ('ref', 'not ilike', 'reklas'), + ('journal_id', '=', 13), + ('state', '=', 'posted'), + ]) + + all_moves = moves | piutangbca | piutangmdr | misc | moves2 for move in all_moves: url = f"/web#id={move.id}&model=account.move&view_type=form" @@ -581,23 +641,54 @@ class RefundSaleOrder(models.Model): ('journal_id', '=', 7), ('state', '=', 'posted'), ]) + misc = self.env['account.move'].search([ + ('ref', 'ilike', all_invoices.mapped('name')[0]), + ('ref', 'not ilike', 'reklas'), + ('journal_id', '=', 13), + ('state', '=', 'posted'), + ]) + moves2 = self.env['account.move'] + if so_ids: + so_records = self.env['sale.order'].browse(so_ids) + so_names = so_records.mapped('name') + + domain = [ + ('journal_id', '=', 11), + ('state', '=', 'posted'), + ('ref', 'ilike', 'dp') + ] + domain += ['|'] * (len(so_names) - 1) + for n in so_names: + domain.append(('ref', 'ilike', n)) + + moves2 = self.env['account.move'].search(domain) + has_moves = bool(moves) + has_moves2 = bool(moves2) has_piutangmdr = bool(piutangmdr) has_piutangbca = bool(piutangbca) + has_misc = bool(misc) 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')) + + amounts = [] + + if has_moves: + amounts.append(sum(moves.mapped('amount_total_signed'))) + if has_moves2: + amounts.append(sum(moves2.mapped('amount_total_signed'))) + if has_piutangbca: + amounts.append(sum(piutangbca.mapped('amount_total_signed'))) + if has_piutangmdr: + amounts.append(sum(piutangmdr.mapped('amount_total_signed'))) + if has_misc: + amounts.append(sum(misc.mapped('amount_total_signed'))) + if has_settlement: + amounts.append(sum(ssos.mapped('gross_amount'))) + + sisa_uang_masuk = sum(amounts) self.uang_masuk = sisa_uang_masuk - amount_refund_before @@ -863,7 +954,7 @@ class RefundSaleOrder(models.Model): if not rec.status or rec.status == 'draft': rec.status = 'pengajuan1' - elif rec.status == 'pengajuan1' and self.env.user.id == 19: + elif rec.status == 'pengajuan1' and self.env.user.id in [19, 28]: rec.status = 'pengajuan2' rec.approved_by = f"{rec.approved_by}, {user_name}" if rec.approved_by else user_name rec.date_approved_sales = now diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index f80941d2..663cba58 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -3340,10 +3340,17 @@ class SaleOrder(models.Model): ('state', '=', 'posted'), ]) + moves2 = self.env['account.move'].search([ + ('ref', 'ilike', self.name), + ('journal_id', '=', 11), + ('state', '=', 'posted'), + ]) + # Default 0 total_uang_muka = 0.0 has_moves = bool(moves) + has_moves2 = bool(moves2) has_piutangmdr = bool(piutangmdr) has_piutangbca = bool(piutangbca) has_settlement = self.payment_status == 'settlement' @@ -3352,6 +3359,8 @@ class SaleOrder(models.Model): total_uang_muka = sum(moves.mapped('amount_total_signed')) + self.gross_amount elif has_moves: total_uang_muka = sum(moves.mapped('amount_total_signed')) + elif has_moves2: + total_uang_muka = sum(moves2.mapped('amount_total_signed')) elif has_settlement: total_uang_muka = self.gross_amount elif has_piutangbca: @@ -3439,12 +3448,20 @@ class SaleOrder(models.Model): ('state', '=', 'posted'), ]) + moves2 = self.env['account.move'].search([ + ('ref', 'ilike', order.name), + ('journal_id', '=', 11), + ('state', '=', 'posted'), + ]) + total_uang_muka = 0.0 if moves and order.payment_status == 'settlement': total_uang_muka = order.gross_amount + sum(moves.mapped('amount_total_signed')) or 0.0 elif moves: total_uang_muka = sum(moves.mapped('amount_total_signed')) or 0.0 + elif moves2: + total_uang_muka = sum(moves2.mapped('amount_total_signed')) or 0.0 elif order.payment_status == 'settlement': total_uang_muka = order.gross_amount else: diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 67106073..b27e6b5d 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -89,8 +89,9 @@ class StockPicking(models.Model): readonly=True, related="id", ) - sj_documentation = fields.Binary(string="Dokumentasi Surat Jalan", ) - paket_documentation = fields.Binary(string="Dokumentasi Paket", ) + sj_documentation = fields.Binary(string="Dokumentasi Surat Jalan") + paket_documentation = fields.Binary(string="Dokumentasi Paket") + dispatch_documentation = fields.Binary(string="Dokumentasi Dispatch") sj_return_date = fields.Datetime(string="SJ Return Date", copy=False) responsible = fields.Many2one('res.users', string='Responsible', tracking=True) diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index c683f75a..cb630a04 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -538,6 +538,11 @@ class TukarGuling(models.Model): self.state = 'approval_sales' def update_doc_state(self): + bu_pick = self.env['stock.picking'].search([ + ('origin', '=', self.operations.origin), + ('name', 'ilike', 'BU/PICK'), + ]) + # OUT tukar guling if self.operations.picking_type_id.id == 29 and self.return_type == 'tukar_guling': total_out = self.env['stock.picking'].search_count([ @@ -552,7 +557,26 @@ class TukarGuling(models.Model): if self.state == 'approved' and total_out > 0 and done_out == total_out: self.state = 'done' - # OUT revisi SO + #SO Lama (gk ada bu pick) + elif self.operations.picking_type_id.id == 29 and self.return_type == 'retur_so' and not bu_pick: + # so_lama = self.env['sale.order'].search([ + # ('name', '=', self.operations.origin), + # ('state', '=', 'done'), + # ('group_id.name', '=', self.operations.origin) + # ]) + total_ort = self.env['stock.picking'].search_count([ + ('tukar_guling_id', '=', self.id), + ('picking_type_id', '=', 74), + ]) + done_srt = self.env['stock.picking'].search([ + ('tukar_guling_id', '=', self.id), + ('picking_type_id', '=', 73), + ('state', '=', 'done') + ]) + if self.state == 'approved' and total_ort == 0 and done_srt and not bu_pick: + self.state = 'done' + + # OUT retur 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), diff --git a/indoteknik_custom/report/purchase_report.xml b/indoteknik_custom/report/purchase_report.xml index 9ff7e718..23fa4d52 100644 --- a/indoteknik_custom/report/purchase_report.xml +++ b/indoteknik_custom/report/purchase_report.xml @@ -97,7 +97,7 @@ <!-- TEKS --> <div style="display:flex; flex-direction:column; flex:1;"> <span style="font-weight:bold; margin-bottom:2px;"> - <t t-esc="line_index + 1"/>. <t t-esc="line.name"/> + <t t-esc="line_index + 1"/>. <t t-esc="line.product_id.display_name"/> </span> </div> </td> diff --git a/indoteknik_custom/views/account_move_views.xml b/indoteknik_custom/views/account_move_views.xml index 7c1f8913..08b93f1f 100644 --- a/indoteknik_custom/views/account_move_views.xml +++ b/indoteknik_custom/views/account_move_views.xml @@ -66,7 +66,8 @@ <group> <group> <field name="partner_id" readonly="1"/> - <field name="day_extension" attrs="{'readonly': [('is_approve', '=', True)]}"/> + <field name="payment_term"/> + <field name="day_extension" attrs="{'readonly': [('is_approve', '=', True)]}"/> <field name="order_id" readonly="1"/> <field name="amount_total" readonly="1"/> </group> diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 0e2eb9e4..21762202 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -235,6 +235,7 @@ <field name='sj_return_date'/> <field name="sj_documentation" widget="image"/> <field name="paket_documentation" widget="image"/> + <field name="dispatch_documentation" widget="image"/> </group> <!-- Biteship Group --> <group attrs="{'invisible': [('select_shipping_option_so', '!=', 'biteship')]}"> |
