summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indoteknik_api/controllers/api_v1/sale_order.py5
-rw-r--r--indoteknik_custom/models/letter_receivable.py29
-rw-r--r--indoteknik_custom/models/refund_sale_order.py28
-rw-r--r--indoteknik_custom/models/sj_tele.py73
-rw-r--r--indoteknik_custom/models/stock_picking.py64
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv6
-rw-r--r--indoteknik_custom/views/account_move_line.xml4
-rw-r--r--indoteknik_custom/views/letter_receivable.xml16
-rw-r--r--indoteknik_custom/views/stock_picking.xml10
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"/>