diff options
| -rw-r--r-- | indoteknik_api/controllers/api_v1/sale_order.py | 5 | ||||
| -rw-r--r-- | indoteknik_custom/models/letter_receivable.py | 29 | ||||
| -rw-r--r-- | indoteknik_custom/models/refund_sale_order.py | 28 | ||||
| -rw-r--r-- | indoteknik_custom/models/sj_tele.py | 73 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_picking.py | 64 | ||||
| -rwxr-xr-x | indoteknik_custom/security/ir.model.access.csv | 6 | ||||
| -rw-r--r-- | indoteknik_custom/views/account_move_line.xml | 4 | ||||
| -rw-r--r-- | indoteknik_custom/views/letter_receivable.xml | 16 | ||||
| -rw-r--r-- | indoteknik_custom/views/stock_picking.xml | 10 |
9 files changed, 159 insertions, 76 deletions
diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index 25268856..1c87400f 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -727,8 +727,9 @@ class SaleOrder(controller.Controller): _logger.info(f"Updated user_id from partner: {parameters['user_id']}") if params['value']['type'] == 'sale_order': - parameters['approval_status'] = 'pengajuan1' - _logger.info("Setting approval_status to 'pengajuan1'") + # parameters['approval_status'] = 'pengajuan1' + parameters['approval_status'] = False + _logger.info("Setting approval_status to 'false'") sale_order = request.env['sale.order'].with_context(from_website_checkout=True).create([parameters]) sale_order.onchange_partner_contact() diff --git a/indoteknik_custom/models/letter_receivable.py b/indoteknik_custom/models/letter_receivable.py index ffe14491..a98e46a1 100644 --- a/indoteknik_custom/models/letter_receivable.py +++ b/indoteknik_custom/models/letter_receivable.py @@ -69,6 +69,18 @@ class SuratPiutang(models.Model): "sp1": "sp2", "sp2": "sp3", } + + def action_select_all_lines(self): + for rec in self: + if not rec.line_ids: + raise UserError(_("Tidak ada invoice line untuk dipilih.")) + rec.line_ids.write({'selected': True}) + + def action_unselect_all_lines(self): + for rec in self: + if not rec.line_ids: + raise UserError(_("Tidak ada invoice line untuk dihapus seleksinya.")) + rec.line_ids.write({'selected': False}) @api.onchange('partner_id') def _onchange_partner_id_domain(self): @@ -193,17 +205,17 @@ class SuratPiutang(models.Model): line.amount_residual or 0.0 for line in rec.line_ids if line.selected ) - @api.constrains("tujuan_email") - def _check_email_format(self): - for rec in self: - if rec.tujuan_email and not mail.single_email_re.match(rec.tujuan_email): - raise ValidationError(_("Format email tidak valid: %s") % rec.tujuan_email) + # @api.constrains("tujuan_email") + # def _check_email_format(self): + # for rec in self: + # if rec.tujuan_email and not mail.single_email_re.match(rec.tujuan_email): + # raise ValidationError(_("Format email tidak valid: %s") % rec.tujuan_email) def action_approve(self): wib = pytz.timezone('Asia/Jakarta') now_wib = datetime.now(wib) - sales_manager_ids = [10] # ganti dengan ID user Sales Manager + sales_manager_ids = [19] # ganti dengan ID user Sales Manager pimpinan_user_ids = [7] # ganti dengan ID user Pimpinan for rec in self: @@ -362,10 +374,11 @@ class SuratPiutang(models.Model): 'subject': subject, # Menggunakan subject yang sudah ditentukan di atas 'email_to': self.tujuan_email, 'email_from': 'finance@indoteknik.co.id', - # 'email_cc': ",".join(sorted(set(cc_list))), + 'email_cc': ",".join(sorted(set(cc_list))), + # 'email_cc': 'finance@indoteknik.co.id', # testing 'body_html': body_html, # Menggunakan body_html yang sudah ditentukan di atas 'attachments': [(attachment.name, attachment.datas)], - # 'reply_to': 'finance@indoteknik.co.id', + 'reply_to': 'finance@indoteknik.co.id', } template.with_context(mail_post_autofollow=False).send_mail( diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py index a866af6e..47565dfc 100644 --- a/indoteknik_custom/models/refund_sale_order.py +++ b/indoteknik_custom/models/refund_sale_order.py @@ -304,12 +304,14 @@ class RefundSaleOrder(models.Model): ('state', '=', 'posted'), ]) - misc = self.env['account.move'].search([ - ('ref', 'ilike', invoices.mapped('name')[0]), - ('ref', 'not ilike', 'reklas'), - ('journal_id', '=', 13), - ('state', '=', 'posted'), - ]) + misc = self.env['account.move'] + if invoices: + 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') @@ -643,12 +645,14 @@ 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'), - ]) + misc = self.env['account.move'] + if all_invoices: + 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) diff --git a/indoteknik_custom/models/sj_tele.py b/indoteknik_custom/models/sj_tele.py index d44aa338..5ea08340 100644 --- a/indoteknik_custom/models/sj_tele.py +++ b/indoteknik_custom/models/sj_tele.py @@ -19,41 +19,18 @@ class SjTele(models.Model): 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}' + # bot_testing = '8306689189:AAHEFe5xwAkapoQ8xKoNZs-6gVfv3kO3kaU' + # chat_id_testing = '-4920864331' + # api_testing = f'https://api.telegram.org/bot{bot_testing}' - data = self.search([], order='create_date asc', limit=15) + data = self.search([], order='create_date asc') if not data: - text = "Berikut merupakan nomor BU/OUT yang belum ada di Logbook SJ report:\nā
tidak ada data (semua sudah tercatat)." + text = "ā
tidak ada data (semua sudah tercatat)." try: r = requests.post(api_base + "/sendMessage", json={'chat_id': chat_id_mqdd, 'text': text}, @@ -74,7 +51,6 @@ class SjTele(models.Model): 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] @@ -84,19 +60,36 @@ class SjTele(models.Model): 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 + BUB = 20 # jumlah baris per bubble + + for i in range(0, len(lines), BUB): + body = "\n".join(lines[i:i + BUB]) + text = header + body + 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 (batch %s-%s): %s", i + 1, min(i + BUB, len(lines)), e) + time.sleep(5) # jeda kecil biar rapi & aman rate limit + + return True + + # 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 0b91e79d..217e76cb 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -176,6 +176,37 @@ class StockPicking(models.Model): linked_manual_bu_out = fields.Many2one('stock.picking', string='BU Out', copy=False) area_name = fields.Char(string="Area", compute="_compute_area_name") + is_bu_iu = fields.Boolean('Is BU/IU', compute='_compute_is_bu_iu', default=False, copy=False, readonl=True) + + @api.constrains('driver_departure_date') + def _constrains_driver_departure_date(self): + allowed_user_ids = [17, 6277, 25] + for record in self: + if record.driver_departure_date and self.env.user.id not in allowed_user_ids: + raise UserError("Hanya Denise dan Faishal yang dapat mengubah Delivery Departure Date.") + + + @api.depends('name') + def _compute_is_bu_iu(self): + for record in self: + if 'BU/IU' in record.name: + record.is_bu_iu = True + else: + record.is_bu_iu = False + + def action_bu_iu_to_pengajuan2(self): + for rec in self: + if not rec.is_bu_iu or not rec.is_internal_use: + raise UserError(_("Tombol ini hanya untuk dokumen BU/IU - Internal Use.")) + if rec.approval_status == False: + raise UserError("Harus Ask Approval terlebih dahulu") + if rec.approval_status in ['pengajuan1'] and self.env.user.is_accounting: + rec.approval_status = 'pengajuan2' + rec.message_post(body=_("Status naik ke Approval Logistik oleh %s") % self.env.user.display_name) + if rec.approval_status in ['pengajuan1', 'pengajuan2', ''] and not self.env.user.is_accounting: + raise UserError("Tombol hanya untuk accounting") + + return True # def _get_biteship_api_key(self): # # return self.env['ir.config_parameter'].sudo().get_param('biteship.api_key_test') @@ -191,7 +222,7 @@ class StockPicking(models.Model): # def write(self, vals): # if 'linked_manual_bu_out' in vals: # for record in self: - # if (record.picking_type_code == 'internal' + # if (record.picking_type_code == 'internal' # and 'BU/PICK/' in record.name): # # Jika menghapus referensi (nilai di-set False/None) # if record.linked_manual_bu_out and not vals['linked_manual_bu_out']: @@ -205,8 +236,8 @@ class StockPicking(models.Model): # @api.model # def create(self, vals): # record = super().create(vals) - # if (record.picking_type_code == 'internal' - # and 'BU/PICK/' in record.name + # if (record.picking_type_code == 'internal' + # and 'BU/PICK/' in record.name # and vals.get('linked_manual_bu_out')): # picking = self.env['stock.picking'].browse(vals['linked_manual_bu_out']) # picking.state_packing = 'packing_done' @@ -511,7 +542,7 @@ class StockPicking(models.Model): # rts_days = rts.days # rts_hours = divmod(rts.seconds, 3600) - # estimated_by_erts = rts.total_seconds() / 3600 + # estimated_by_erts = rts.total_seconds() / 3600 # record.countdown_ready_to_ship = f"{rts_days} days, {rts_hours} hours" # record.countdown_hours = estimated_by_erts @@ -1080,10 +1111,18 @@ class StockPicking(models.Model): return res + def ask_approval(self): - if self.env.user.is_accounting: + # if self.env.user.is_accounting: + # if self.env.user.is_accounting and self.location_id.id == 57 or self.location_id == 57 and self.approval_status in ['pengajuan1', ''] and 'BU/IU' in self.name and self.approval_status == 'pengajuan1': + # raise UserError("Bisa langsung set ke approval logistik") + if self.env.user.is_accounting and self.approval_status == "pengajuan2" and 'BU/IU' in self.name: + raise UserError("Tidak perlu ask approval sudah approval logistik") + if self.env.user.is_logistic_approver and self.location_id.id == 57 or self.location_id== 57 and self.approval_status == 'pengajuan2' and 'BU/IU' in self.name: raise UserError("Bisa langsung Validate") + + # for calendar distribute only # if self.is_internal_use: # stock_move_lines = self.env['stock.move.line'].search([ @@ -1106,6 +1145,7 @@ class StockPicking(models.Model): raise UserError("Qty tidak boleh 0") pick.approval_status = 'pengajuan1' + def ask_receipt_approval(self): if self.env.user.is_logistic_approver: raise UserError('Bisa langsung validate tanpa Ask Receipt') @@ -1307,7 +1347,16 @@ class StockPicking(models.Model): if self.picking_type_id.code == 'incoming' and self.group_id.id == False and self.is_internal_use == False: raise UserError(_('Tidak bisa Validate jika tidak dari Document SO / PO')) - if self.is_internal_use and not self.env.user.is_accounting: + # if self.is_internal_use and not self.env.user.is_logistic_approver and self.location_id.id == 57 and self.approval_status == 'pengajuan2': + # raise UserError("Harus di Approve oleh Logistik") + + if self.is_internal_use and self.approval_status in ['pengajuan1', '', False] and 'BU/IU' in self.name and self.is_bu_iu == True: + raise UserError("Tidak Bisa Validate, set approval status ke approval logistik terlebih dahhulu") + + if self.is_internal_use and not self.env.user.is_logistic_approver and self.approval_status in ['pengajuan2'] and self.is_bu_iu == True and 'BU/IU' in self.name: + raise UserError("Harus di Approve oleh Logistik") + + if self.is_internal_use and not self.env.user.is_accounting and self.approval_status in ['pengajuan1', '', False] and self.is_bu_iu == False: raise UserError("Harus di Approve oleh Accounting") if self.picking_type_id.id == 28 and not self.env.user.is_logistic_approver: @@ -1316,7 +1365,7 @@ class StockPicking(models.Model): if self.location_dest_id.id == 47 and not self.env.user.is_purchasing_manager: raise UserError("Transfer ke gudang selisih harus di approve Rafly Hanggara") - if self.is_internal_use: + if self.is_internal_use and self.approval_status == 'pengajuan2': self.approval_status = 'approved' elif self.picking_type_id.code == 'incoming': self.approval_receipt_status = 'approved' @@ -1333,6 +1382,7 @@ class StockPicking(models.Model): current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') self.date_reserved = current_time + # Validate Qty Demand Can't higher than Qty Product if self.location_dest_id.id == 58 and 'BU/INPUT/' in self.name: for move in self.move_ids_without_package: diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index fa519232..b30b898b 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -204,7 +204,7 @@ access_tukar_guling_mapping_koli_all_users,tukar.guling.mapping.koli.all.users,m 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 +access_unpaid_invoice_view,access.unpaid.invoice.view,model_unpaid_invoice_view,,1,1,1,1 +access_surat_piutang_user,surat.piutang user,model_surat_piutang,,1,1,1,1 +access_surat_piutang_line_user,surat.piutang.line user,model_surat_piutang_line,,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/account_move_line.xml b/indoteknik_custom/views/account_move_line.xml index 017a9eda..3a20388e 100644 --- a/indoteknik_custom/views/account_move_line.xml +++ b/indoteknik_custom/views/account_move_line.xml @@ -6,9 +6,9 @@ <field name="model">account.move</field> <field name="inherit_id" ref="account.view_move_form"/> <field name="arch" type="xml"> - <xpath expr="//page[@id='aml_tab']/field[@name='line_ids']" position="attributes"> + <!-- <xpath expr="//page[@id='aml_tab']/field[@name='line_ids']" position="attributes"> <attribute name="attrs">{'readonly': [('refund_id','!=',False)]}</attribute> - </xpath> + </xpath> --> <xpath expr="//page[@id='aml_tab']/field[@name='line_ids']/tree/field[@name='currency_id']" position="before"> <field name="is_required" invisible="1"/> </xpath> diff --git a/indoteknik_custom/views/letter_receivable.xml b/indoteknik_custom/views/letter_receivable.xml index 98186862..3241d5f1 100644 --- a/indoteknik_custom/views/letter_receivable.xml +++ b/indoteknik_custom/views/letter_receivable.xml @@ -128,11 +128,23 @@ </div> </div> <div> + <button name="action_select_all_lines" + type="object" + string="Select All" + class="btn btn-secondary" + icon="fa-check-square"/> + <button name="action_unselect_all_lines" + type="object" + string="Unselect All" + class="btn btn-secondary" + icon="fa-square-o" + style="margin-left:5px;"/> <button name="action_refresh_lines" string="Refresh Invoices" type="object" - class="btn-primary" - style="margin-left:10px;" + class="btn btn-secondary" + icon="fa-refresh" + style="margin-left:5px;" help="Refresh Invoices agar data tetap update"/> </div> </div> diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 21762202..d943f27a 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -45,6 +45,14 @@ <field name="model">stock.picking</field> <field name="inherit_id" ref="stock.view_picking_form"/> <field name="arch" type="xml"> + <!-- Tambahkan tombol custom: tampil hanya saat BU/IU + pengajuan1 --> + <xpath expr="//header" position="inside"> + <button name="action_bu_iu_to_pengajuan2" + type="object" + string="Approve by Accounting" + class="btn-primary" + attrs="{'invisible': [('is_bu_iu', '=', False), ('approval_status', 'in', ['pengajuan2', False, 'false', ''])]}"/> + </xpath> <button name="action_confirm" position="before"> <button name="ask_approval" string="Ask Approval" @@ -158,6 +166,8 @@ <field name="purchase_id"/> <field name="sale_order"/> <field name="invoice_status"/> + <field name="is_bu_iu" /> + <field name="approval_status" attrs="{'invisible': [('is_bu_iu', '=', False)]}"/> <field name="date_doc_kirim" attrs="{'readonly':[('invoice_status', '=', 'invoiced')]}"/> <field name="summary_qty_operation"/> <field name="count_line_operation"/> |
