summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAzka Nathan <darizkyfaz@gmail.com>2026-02-25 09:41:17 +0700
committerAzka Nathan <darizkyfaz@gmail.com>2026-02-25 09:41:17 +0700
commita3ff90b7a2616a01535fc1968d4ac5bfdf9f3c26 (patch)
treed3eb7a90aee08085c04a4ebe5343b46e4ac2dc20
parent316b8257845d0df10153fa7e5e294a699ad17c56 (diff)
parent75972376c64d133e4dcf8cb0808a60b36db07825 (diff)
Merge branch 'odoo-backup' into feature/api-altama-matches-so
# Conflicts: # indoteknik_custom/__manifest__.py # indoteknik_custom/models/__init__.py
-rwxr-xr-xindoteknik_custom/__manifest__.py4
-rwxr-xr-xindoteknik_custom/models/__init__.py2
-rw-r--r--indoteknik_custom/models/gudang_service.py273
-rw-r--r--indoteknik_custom/models/kartu_stock.py186
-rw-r--r--indoteknik_custom/models/keywords.py31
-rwxr-xr-xindoteknik_custom/models/purchase_order.py3
-rw-r--r--indoteknik_custom/models/tukar_guling.py61
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv3
-rw-r--r--indoteknik_custom/views/account_move_line.xml3
-rw-r--r--indoteknik_custom/views/gudang_service.xml110
-rw-r--r--indoteknik_custom/views/ir_sequence.xml9
-rw-r--r--indoteknik_custom/views/kartu_stock.xml17
12 files changed, 669 insertions, 33 deletions
diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py
index 1ad1e52e..88abc162 100755
--- a/indoteknik_custom/__manifest__.py
+++ b/indoteknik_custom/__manifest__.py
@@ -8,7 +8,7 @@
'author': 'Rafi Zadanly',
'website': '',
'images': ['assets/favicon.ico'],
- 'depends': ['base', 'coupon', 'delivery', 'sale', 'sale_management', 'vit_kelurahan', 'vit_efaktur'],
+ 'depends': ['base', 'coupon', 'delivery', 'sale', 'sale_management', 'vit_kelurahan', 'vit_efaktur', 'proweb_kartu_stok'],
'data': [
'views/assets.xml',
'security/ir.model.access.csv',
@@ -193,6 +193,8 @@
'views/commission_internal.xml',
'views/keywords.xml',
'views/token_log.xml'
+ 'views/gudang_service.xml',
+ 'views/kartu_stock.xml',
],
'demo': [],
'css': [],
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py
index faea989d..31ee5108 100755
--- a/indoteknik_custom/models/__init__.py
+++ b/indoteknik_custom/models/__init__.py
@@ -165,6 +165,8 @@ from . import partial_delivery
from . import domain_apo
from . import uom_uom
from . import commission_internal
+from . import gudang_service
from . import update_depreciation_move_wizard
from . import keywords
from . import token_log
+from . import kartu_stock
diff --git a/indoteknik_custom/models/gudang_service.py b/indoteknik_custom/models/gudang_service.py
new file mode 100644
index 00000000..d699ccf4
--- /dev/null
+++ b/indoteknik_custom/models/gudang_service.py
@@ -0,0 +1,273 @@
+from odoo import models, fields, api, _
+from odoo.exceptions import UserError, ValidationError
+import logging
+from datetime import datetime
+from collections import defaultdict
+
+
+class GudangService(models.Model):
+ _name = "gudang.service"
+ _description = "Gudang Service"
+ _inherit = ["mail.thread", "mail.activity.mixin"]
+ _order = "id asc"
+
+ name = fields.Char("Name", readonly=True)
+ partner_id = fields.Many2one("res.partner", string="Customer", readonly=True)
+ vendor_id = fields.Many2one("res.partner", string="Vendor Service", required=True)
+ origin = fields.Many2one(
+ "sale.order",
+ string="Origin SO",
+ required=True,
+ domain=[("state", "in", ["done", "sale"])],
+ )
+ schedule_date = fields.Date(string="Schedule Date", required=True, tracking=True)
+ start_date = fields.Datetime(string="Date Processed", copy=False, tracking=True)
+ create_date = fields.Datetime(
+ string="Create Date", copy=False, tracking=True, default=fields.Datetime.now()
+ )
+ done_date = fields.Datetime(string="Date Done", copy=False, tracking=True)
+ gudang_service_lines = fields.One2many(
+ "gudang.service.line", "gudang_service_id", string="Gudang Service Lines"
+ )
+ # unprocessed_date = fields.Char(
+ # string='Unprocessed Since',
+ # compute='_compute_unprocessed_date'
+ # )
+ remaining_date = fields.Char(
+ compute="_compute_remaining_date", string="Date Status"
+ )
+ state = fields.Selection(
+ [
+ ("draft", "Backlog"),
+ ("received_from_cust", "Received From Customer"),
+ ("sent_to_vendor", "Sent to Service Vendor"),
+ ("received_from_vendor", "Received From Service Vendor"),
+ ("delivered_to_cust", "Delivered to Customer"),
+ ("cancel", "Cancel"),
+ ],
+ default="draft",
+ tracking=True,
+ )
+ cancel_reason = fields.Text("Cancel Reason", tracking=True)
+
+ def check_duplicate_docs(self):
+ for rec in self:
+ found = self.env["gudang.service"].search(
+ [
+ ("id", "!=", self.id),
+ ("origin.id", "=", self.origin.id),
+ ("partner_id.id", "=", rec.partner_id.id),
+ ("vendor_id.id", "=", rec.vendor_id.id),
+ ("schedule_date", "=", rec.schedule_date),
+ (
+ "gudang_service_lines.product_id.name",
+ "=",
+ rec.gudang_service_lines.product_id.name,
+ ),
+ (
+ "gudang_service_lines.quantity",
+ "=",
+ rec.gudang_service_lines.quantity,
+ ),
+ ]
+ )
+ if found:
+ raise UserError("This Document has duplicate with %s" % found.name)
+
+ def _send_logistic_notification(self):
+ group = self.env.ref(
+ "indoteknik_custom.group_role_logistic", raise_if_not_found=False
+ )
+ if not group:
+ return
+
+ users = group.users
+ # MD
+ md = self.env["res.users"].browse([3425, 4801, 1036])
+ # send to logistic and MD
+ users = users | md
+
+ if not users:
+ return
+
+ # Logistic users to be excluded
+ excluded_users = [7, 17098, 216, 28, 15710]
+
+ for rec in self:
+ for user in users:
+ if user.id in excluded_users:
+ continue
+ self.env["mail.activity"].create(
+ {
+ "res_model_id": self.env["ir.model"]._get_id("gudang.service"),
+ "res_id": rec.id,
+ "activity_type_id": self.env.ref(
+ "mail.mail_activity_data_todo"
+ ).id,
+ "user_id": user.id,
+ "summary": "Gudang Service On Progress",
+ "note": _(
+ "Ada Jadwal Service Barang di Document <b>%s</b> Jadwal Service 📅 <b>%s</b>"
+ )
+ % (rec.name, rec.schedule_date),
+ # 'date_deadline': fields.Date.today(),
+ }
+ )
+
+ # kirim ke private message odoo
+ channel = (
+ self.env["mail.channel"]
+ .channel_get([self.env.user.partner_id.id, user.partner_id.id])
+ .get("id")
+ )
+ if not channel:
+ continue
+ res = self.env["mail.channel"].browse(channel)
+ res.with_user(self.env.user.browse(25)).message_post(
+ body=_(
+ "Ada Jadwal Service Barang di Document <b>%s</b> Jadwal Service 📅 <b>%s</b>"
+ )
+ % (rec.name, rec.schedule_date),
+ message_type="comment",
+ subtype_xmlid="mail.mt_comment",
+ )
+
+ @api.model
+ def cron_notify_onprogress_gudang_service(self):
+ records = self.search(
+ [
+ ("state", "in", ["draft", "received_from_customer"]),
+ ]
+ )
+
+ if records:
+ records._send_logistic_notification()
+
+ @api.depends("schedule_date", "create_date")
+ def _compute_remaining_date(self):
+ today = fields.Date.today()
+
+ for rec in self:
+ if not rec.schedule_date:
+ rec.remaining_date = "-"
+ continue
+
+ base_date = rec.create_date.date() if rec.create_date else today
+
+ schedule = rec.schedule_date
+ days = (schedule - base_date).days
+
+ if days > 0:
+ rec.remaining_date = _("In %s days") % days
+ elif days == 0:
+ rec.remaining_date = _("Today")
+ else:
+ rec.remaining_date = _("Overdue %s days") % abs(days)
+
+ def action_submit(self):
+ self.ensure_one()
+ self.check_duplicate_docs()
+ for rec in self:
+ if rec.state == "draft":
+ rec.state = "received_from_cust"
+ elif rec.state == "received_from_cust":
+ rec.state = "sent_to_vendor"
+ rec.start_date = fields.Datetime.now()
+ elif rec.state == "sent_to_vendor":
+ rec.state = "received_from_vendor"
+
+ def action_done(self):
+ for rec in self:
+ if rec.state != "received_from_vendor":
+ raise UserError("Only 'Received From Vendor' state can be set to Done")
+
+ rec.activity_ids.unlink()
+
+ rec.write(
+ {"state": "delivered_to_cust", "done_date": fields.Datetime.now()}
+ )
+
+ def action_draft(self):
+ """Reset to draft state"""
+ for rec in self:
+ rec.cancel_reason = False
+ if rec.state == "cancel":
+ rec.write({"state": "draft"})
+ else:
+ raise UserError("Only Canceled Record Can Be Reset To Draft")
+
+ def action_cancel(self):
+ for rec in self:
+ activities = self.env["mail.activity"].search(
+ [
+ ("res_id", "=", rec.id),
+ ("res_model", "=", "gudang.service"),
+ ]
+ )
+ activities.unlink()
+ if rec.state == "delivered_to_cust":
+ raise UserError("You cannot cancel a done record")
+ if not rec.cancel_reason:
+ raise UserError("Cancel Reason must be filled")
+ rec.start_date = False
+ rec.done_date = False
+ rec.state = "cancel"
+
+ @api.model
+ def create(self, vals):
+ # sequence
+ if not vals.get("name") or vals["name"] == "New":
+ vals["name"] = self.env["ir.sequence"].next_by_code("gudang.service")
+
+ # partner dari SO
+ so = self.env["sale.order"].browse(vals["origin"])
+ vals["partner_id"] = so.partner_id.id
+
+ res = super(GudangService, self).create(vals)
+
+ res.check_duplicate_docs()
+ res._send_logistic_notification()
+ return res
+
+ def write(self, vals):
+ if vals.get("origin"):
+ so = self.env["sale.order"].browse(vals["origin"])
+ vals["partner_id"] = so.partner_id.id
+ vals.check_duplicate_docs()
+
+ return super(GudangService, self).write(vals)
+
+ @api.onchange("origin")
+ def _onchange_origin(self):
+ if not self.origin:
+ self.gudang_service_lines = [(5, 0, 0)]
+ return
+
+ self.partner_id = self.origin.partner_id
+
+ lines = []
+ for line in self.origin.order_line:
+ lines.append(
+ (
+ 0,
+ 0,
+ {
+ "product_id": line.product_id.id,
+ "quantity": line.product_uom_qty,
+ "origin_so": self.origin.id,
+ },
+ )
+ )
+
+ # hapus line lama lalu isi baru
+ self.gudang_service_lines = [(5, 0, 0)] + lines
+
+
+class GudangServiceLine(models.Model):
+ _name = "gudang.service.line"
+ _inherit = ["mail.thread", "mail.activity.mixin"]
+
+ product_id = fields.Many2one("product.product", string="Product")
+ quantity = fields.Float(string="Quantity")
+ origin_so = fields.Many2one("sale.order", string="Origin SO")
+ gudang_service_id = fields.Many2one("gudang.service", string="Gudang Service ID")
diff --git a/indoteknik_custom/models/kartu_stock.py b/indoteknik_custom/models/kartu_stock.py
new file mode 100644
index 00000000..22f90df0
--- /dev/null
+++ b/indoteknik_custom/models/kartu_stock.py
@@ -0,0 +1,186 @@
+from odoo import models
+from io import BytesIO
+import datetime
+from base64 import encodebytes
+import xlsxwriter
+
+
+class KartuStokWizardInherit(models.TransientModel):
+ _inherit = 'kartu.stok.wizard'
+
+
+ def action_kartu_stok_excel_single_sheet(self):
+
+ active_ids_tmp = self.env.context.get('active_ids')
+ active_model = self.env.context.get('active_model')
+
+ if active_model == 'product.template':
+ active_ids = self.env['product.product'].search(
+ [('product_tmpl_id', 'in', active_ids_tmp),
+ ('active', '=', True)]).ids
+ else:
+ active_ids = active_ids_tmp
+
+ data = {
+ 'location_id': self.location_id.id,
+ 'day_date': self.day_date,
+ 'previous_number_days': self.previous_number_days,
+ 'date_from': self.date_from,
+ 'date_to': self.date_to,
+ 'ids': active_ids,
+ 'context': {'active_model': active_model}
+ }
+
+ file_io = BytesIO()
+ workbook = xlsxwriter.Workbook(file_io)
+
+ self.generate_xlsx_single_sheet(workbook, data)
+
+ workbook.close()
+
+ fout = encodebytes(file_io.getvalue())
+ datetime_string = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
+ filename = f'Kartu_Stok_Single_{datetime_string}.xlsx'
+
+ self.write({
+ 'fileout': fout,
+ 'fileout_filename': filename
+ })
+
+ file_io.close()
+
+ return {
+ 'type': 'ir.actions.act_url',
+ 'target': 'new',
+ 'url': 'web/content/?model=' + self._name +
+ '&id=' + str(self.id) +
+ '&field=fileout&download=true&filename=' + filename,
+ }
+
+ def generate_xlsx_single_sheet(self, workbook, data):
+
+ bold = workbook.add_format({'bold': True})
+ border_date_right = workbook.add_format({'border':1, 'num_format': 'DD-MM', 'bg_color': '#dddddd', 'align': 'right'})
+ border_int_right = workbook.add_format({'border':1, 'num_format': '#,##0', 'bg_color': '#dddddd', 'align': 'right'})
+ border_int_right_bold = workbook.add_format({'border':1, 'num_format': '#,##0', 'bg_color': '#dddddd', 'align': 'right', 'bold': True})
+ border_text_center = workbook.add_format({'border':1, 'bg_color': '#dddddd', 'align': 'center'})
+ header_format = workbook.add_format({'bold': True, 'border':1, 'bg_color': '#808080', 'align': 'center'})
+
+ sheet = workbook.add_worksheet('Kartu Stok')
+
+ docs = self.env['product.product'].browse(data['ids'])
+ location = self.env['stock.location'].browse(data['location_id'])
+ location_name = location.display_name.split('/')[0]
+
+ row = 0
+
+ for doc in docs:
+
+ # =========================
+ # HEADER PRODUCT
+ # =========================
+ sheet.write(row, 0, doc.display_name, bold)
+ row += 1
+ sheet.write(row, 0, location_name, bold)
+ row += 2
+
+ # =========================
+ # TABLE HEADER
+ # =========================
+ sheet.write(row, 0, 'Date', header_format)
+ sheet.write(row, 1, 'In', header_format)
+ sheet.write(row, 2, 'Out', header_format)
+ sheet.write(row, 3, 'Stock', header_format)
+ sheet.write(row, 4, 'Distributor', header_format)
+ sheet.write(row, 5, 'Buyer', header_format)
+ sheet.write(row, 6, 'Document', header_format)
+ sheet.write(row, 7, 'Source Document', header_format)
+ row += 1
+
+ stock_total = 0
+ stock_show_initial = False
+
+ # =========================
+ # MOVE LOOP (SAMA LOGIC ASLI)
+ # =========================
+ for move in doc.stock_move_ids.sorted(key=lambda sm: sm.date):
+ for line in move.move_line_ids:
+
+ if line.state != 'done':
+ continue
+
+ if line.location_id.id != data['location_id'] and \
+ line.location_dest_id.id != data['location_id']:
+ continue
+
+ if not stock_show_initial:
+ sheet.write(row, 3, stock_total, border_int_right_bold)
+ sheet.write(row, 4, 'Initial Stock', border_text_center)
+ stock_show_initial = True
+ row += 1
+
+ qty_in = 0
+ qty_out = 0
+
+ if line.location_dest_id.id == data['location_id']:
+ qty_in = line.qty_done
+ stock_total += qty_in
+
+ if line.location_id.id == data['location_id']:
+ qty_out = line.qty_done
+ stock_total -= qty_out
+
+ sheet.write(row, 0, line.date, border_date_right)
+ sheet.write(row, 1, qty_in, border_int_right)
+ sheet.write(row, 2, qty_out, border_int_right)
+ sheet.write(row, 3, stock_total, border_int_right)
+ # Distributor
+ col = 4
+ if line.location_dest_id.id == data['location_id']:
+ if line.picking_id and line.picking_id.origin:
+ sheet.write(row, col, line.picking_id.partner_id.display_name, border_text_center)
+ else:
+ if line.location_id:
+ if line.location_id.name == 'Inventory adjustment':
+ sheet.write(row, col, 'Adjust *', border_text_center)
+ else:
+ sheet.write(row, col, line.location_id.location_id.name + ' *', border_text_center)
+ else:
+ sheet.write(row, col, doc.seller_ids[0].name + ' *' if doc.seller_ids else '', border_text_center)
+ else:
+ sheet.write(row, col, '', border_text_center)
+ # Buyer
+ col = 5
+ if line.location_id.id == data['location_id']:
+ if line.picking_id and line.picking_id.origin:
+ sheet.write(row, col, line.picking_id.partner_id.display_name, border_text_center)
+ else:
+ if line.location_dest_id:
+ if line.location_dest_id.name == 'Inventory adjustment':
+ sheet.write(row, col, 'Adjust *', border_text_center)
+ else:
+ sheet.write(row, col, line.location_dest_id.location_id.name + ' *', border_text_center)
+ else:
+ sheet.write(row, col, doc.seller_ids[0].name + ' *' if doc.seller_ids else '', border_text_center)
+ else:
+ sheet.write(row, col, '', border_text_center)
+ # Document
+ col = 6
+ if line.picking_id and line.picking_id.origin:
+ sheet.write(row, col, line.picking_id.name, border_text_center)
+ else:
+ sheet.write(row, col, line.reference or '', border_text_center)
+ # Source Document
+ col = 7
+ if line.picking_id and line.picking_id.origin:
+ sheet.write(row, col, line.picking_id.origin, border_text_center)
+ else:
+ sheet.write(row, col, line.reference or '', border_text_center)
+
+ row += 1
+
+ row += 3 # jarak antar product
+
+
+
+
diff --git a/indoteknik_custom/models/keywords.py b/indoteknik_custom/models/keywords.py
index 2ee217f7..3fa9dd72 100644
--- a/indoteknik_custom/models/keywords.py
+++ b/indoteknik_custom/models/keywords.py
@@ -160,37 +160,6 @@ class Keywords(models.Model):
return True
- # Old
- # def _sync_keywords_queue_callback(self):
- # """Callback method executed by apache.solr.queue - syncs keyword data to Solr"""
- # documents = []
- # for keyword in self:
- # # Skip syncing if product count is 0
- # if len(keyword.product_ids) == 0:
- # _logger.info('Skipping Solr sync for keyword "%s" - no products found', keyword.keywords)
- # continue
-
- # searchkey = (keyword.keywords or '').strip().lower().replace(' ', '-')
- # try:
- # doc = {
- # 'id': keyword.id,
- # 'category_id_i': keyword.category_id.id,
- # 'keywords_s': searchkey,
- # 'url_s': keyword.url,
- # 'product_ids_is': [p.product_tmpl_id.id for p in keyword.product_ids],
- # }
- # documents.append(doc)
- # except Exception as e:
- # _logger.error('failed %s', e)
- # _logger.error('doc data: %s', doc)
-
- # if documents:
- # solr.add(documents)
-
- # self.write({'solr_flag': 0})
-
- # return True
-
def _sync_keywords_queue_callback(self):
success_keywords = self.browse()
diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py
index dd0c5cd5..b0fea18b 100755
--- a/indoteknik_custom/models/purchase_order.py
+++ b/indoteknik_custom/models/purchase_order.py
@@ -931,7 +931,8 @@ class PurchaseOrder(models.Model):
date_done = self.date_approve
- day_extension = int(self.payment_term_id.line_ids.days)
+ # day_extension = int(self.payment_term_id.line_ids.days)
+ day_extension = int(max(self.payment_term_id.line_ids.mapped('days'), default=0))
payment_schedule = date_done + timedelta(days=day_extension)
if payment_schedule.weekday() == 0:
diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py
index 682c478a..619e7c99 100644
--- a/indoteknik_custom/models/tukar_guling.py
+++ b/indoteknik_custom/models/tukar_guling.py
@@ -62,6 +62,7 @@ class TukarGuling(models.Model):
notes = fields.Text('Notes')
return_type = fields.Selection(String='Return Type', selection=[
('tukar_guling', 'Tukar Guling'), # -> barang yang sama
+ # ('service', 'Service'), # -> barang yang sama
('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'),
@@ -931,6 +932,66 @@ class TukarGuling(models.Model):
_logger.info(f"✅ BU/PICK Baru dari ORT created: {new_pick.name}")
record.message_post(
body=f"📦 <b>{new_pick.name}</b> created by <b>{self.env.user.name}</b> (state: <b>{new_pick.state}</b>)")
+
+ # if record.return_type == 'service':
+ # GUDANG_SERVICE_LOCATION_ID = 98
+ # # From STOCK to OUTPUT
+ # done_service = self.env['stock.picking'].create({
+ # 'group_id': bu_out.group_id.id,
+ # 'tukar_guling_id': record.id,
+ # 'sale_order': record.origin,
+ # 'note': record.notes,
+ # 'picking_type_id': 32,
+ # 'location_id': GUDANG_SERVICE_LOCATION_ID,
+ # 'location_dest_id': BU_STOCK_LOCATION_ID,
+ # 'partner_id': bu_out.partner_id.id,
+ # 'move_ids_without_package': [(0, 0, {
+ # 'product_id': line.product_id.id,
+ # 'product_uom_qty': line.product_uom_qty,
+ # 'product_uom': line.product_uom.id,
+ # 'name': line.product_id.display_name,
+ # 'location_id': GUDANG_SERVICE_LOCATION_ID,
+ # 'location_dest_id': BU_STOCK_LOCATION_ID,
+ # }) for line in record.line_ids],
+ # })
+ # if done_service:
+ # done_service.action_confirm()
+ # done_service.action_assign()
+ # else:
+ # raise UserError("Gagal membuat picking service")
+
+ # service_to_output = self.env['stock.picking'].create({
+ # 'group_id': bu_out.group_id.id,
+ # 'tukar_guling_id': record.id,
+ # 'sale_order': record.origin,
+ # 'note': record.notes,
+ # 'picking_type_id': 32,
+ # 'location_id': BU_STOCK_LOCATION_ID,
+ # 'location_dest_id': BU_OUTPUT_LOCATION_ID,
+ # 'partner_id': bu_out.partner_id.id,
+ # 'move_lines': [(0, 0, {
+ # 'product_id': line.product_id.id,
+ # 'product_uom_qty': line.product_uom_qty,
+ # 'product_uom': line.product_uom.id,
+ # 'name': line.product_id.display_name,
+ # 'location_id':BU_STOCK_LOCATION_ID,
+ # 'location_dest_id': BU_STOCK_LOCATION_ID,
+ # }) for line in record.line_ids],
+ # 'move_ids_without_package': [(0, 0, {
+ # 'product_id': line.product_id.id,
+ # 'product_uom_qty': line.product_uom_qty,
+ # 'product_uom': line.product_uom.id,
+ # 'name': line.product_id.display_name,
+ # 'location_id': BU_STOCK_LOCATION_ID,
+ # 'location_dest_id': BU_OUTPUT_LOCATION_ID,
+ # }) for line in record.line_ids],
+ # })
+ # if service_to_output:
+ # service_to_output.action_confirm()
+ # service_to_output.action_assign()
+ # else:
+ # raise UserError("Gagal membuat picking service")
+
# BU/OUT Baru dari SRT
if srt_picking:
diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv
index c1935c36..42c68e80 100755
--- a/indoteknik_custom/security/ir.model.access.csv
+++ b/indoteknik_custom/security/ir.model.access.csv
@@ -216,6 +216,9 @@ 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
access_stock_picking_sj_document,stock.picking.sj.document,model_stock_picking_sj_document,base.group_user,1,1,1,1
+access_gudang_service,gudang.service,model_gudang_service,base.group_user,1,1,1,1
+access_gudang_service_line,gudang.service.line,model_gudang_service_line,base.group_user,1,1,1,1
access_update_depreciation_move_wizard,access.update.depreciation.move.wizard,model_update_depreciation_move_wizard,,1,1,1,1
+
access_keywords,keywords,model_keywords,base.group_user,1,1,1,1
access_token_log,access.token.log,model_token_log,,1,1,1,1
diff --git a/indoteknik_custom/views/account_move_line.xml b/indoteknik_custom/views/account_move_line.xml
index 838596c8..346494f3 100644
--- a/indoteknik_custom/views/account_move_line.xml
+++ b/indoteknik_custom/views/account_move_line.xml
@@ -9,6 +9,9 @@
<!-- <xpath expr="//page[@id='aml_tab']/field[@name='line_ids']" position="attributes">
<attribute name="attrs">{'readonly': [('refund_id','!=',False)]}</attribute>
</xpath> -->
+ <xpath expr="//field[@name='line_ids']/tree/field[@name='credit']" position="after">
+ <field name="date_maturity" optional="hide"/>
+ </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/gudang_service.xml b/indoteknik_custom/views/gudang_service.xml
new file mode 100644
index 00000000..769664c5
--- /dev/null
+++ b/indoteknik_custom/views/gudang_service.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<odoo>
+ <data>
+ <!-- Tree -->
+ <record id="view_gudang_service_tree" model="ir.ui.view">
+ <field name="name">gudang.serivice.tree</field>
+ <field name="model">gudang.service</field>
+ <field name="arch" type="xml">
+ <tree string="Monitoring Barang Service" decoration-danger="state in ('draft', 'received_from_cust')" decoration-warning="state in ('sent_to_vendor', 'received_from_vendor')"
+ decoration-success="state == 'delivered_to_cust'" decoration-muted="state == 'cancel'" >
+ <field name="name"/>
+ <field name="partner_id"/>
+ <field name="vendor_id"/>
+ <field name="origin"/>
+ <field name="schedule_date"/>
+ <field name="start_date" optional="hide"/>
+ <field name="remaining_date"/>
+ <field name="state" widget="badge" decoration-danger="state in ('draft', 'received_from_cust')" decoration-warning="state in ('sent_to_vendor', 'received_from_vendor')"
+ decoration-success="state == 'delivered_to_cust'" decoration-muted="state == 'cancel'" />
+ <field name="cancel_reason" optional="hide"/>
+ <field name="create_date" optional="hide"/>
+ </tree>
+ </field>
+ </record>
+ <!-- Form -->
+ <record id="view_gudang_service_form" model="ir.ui.view">
+ <field name="name">gudang.service.form</field>
+ <field name="model">gudang.service</field>
+ <field name="arch" type="xml">
+ <form>
+ <header>
+ <button name="action_submit" string="Proceed" type="object"
+ class="btn-primary"
+ attrs="{'invisible': [('state', 'in', ['cancel', 'done', 'received_from_vendor', 'delivered_to_cust'])]}"/>
+ <button name="action_done" string="Set Done" type="object"
+ class="btn-primary"
+ attrs="{'invisible': [('state', 'not in', ['received_from_vendor'])]}"/>
+ <button name="action_cancel" string="Cancel" type="object"
+ class="btn-secondary"
+ attrs="{'invisible': [('state', 'in', ['cancel', 'delivered_to_cust'])]}"/>
+ <button name="action_draft" string="Set to Backlog" type="object"
+ class="btn-secondary"
+ attrs="{'invisible': [('state', 'not in', ['cancel'])]}"/>
+ <field name="state" widget="statusbar" readonly="1"/>
+ </header>
+ <sheet>
+ <div class="oe_title">
+ <h1>
+ <field name="name" readonly="1" class="oe_inline"/>
+ </h1>
+ </div>
+ <group>
+ <field name="origin" attrs="{'readonly': [('state', 'not in', ['draft'])]}"/>
+ <field name="partner_id"/>
+ <field name="vendor_id"/>
+ <field name="remaining_date"/>
+ <field name="schedule_date" attrs="{'readonly': [('state', 'not in', ['draft', 'reveived_from_cust'])]}"/>
+ <field name="start_date" readonly="1"/>
+ <field name="done_date" attrs="{'invisible': [('state', 'not in', ['delivered_to_cust'])]}"/>
+ <field name="create_uid"/>
+ <field name="cancel_reason"
+ attrs="{'invisible': [('state', 'in', ['delivered_to_cust', 'draft'])]}"/>
+ </group>
+ <notebook>
+ <page string="Product Lines" name="product_lines">
+ <field name="gudang_service_lines">
+ <tree string="Product Lines" editable="top" create="0" delete="1">
+ <field name="product_id"/>
+ <field name="quantity"/>
+ </tree>
+ </field>
+ </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>
+ <!-- Action -->
+ <record id="action_gudang_service" model="ir.actions.act_window">
+ <field name="name">Monitoring Barang Service</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">gudang.service</field>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <!-- Menu -->
+ <menuitem
+ id="menu_gudang_service"
+ name="Monitoring Barang Service"
+ parent="indoteknik_custom.menu_monitoring_in_sale"
+ sequence="10"
+ action="action_gudang_service"
+ />
+ </data>
+ <!-- Cron -->
+ <record id="ir_cron_gudang_service_logistik_notify" model="ir.cron">
+ <field name="name">Gudang Service Daily Notification</field>
+ <field name="model_id" ref="model_gudang_service"/>
+ <field name="state">code</field>
+ <field name="code">model.cron_notify_onprogress_gudang_service()</field>
+ <field name="interval_number">1</field>
+ <field name="interval_type">days</field>
+ <field name="numbercall">-1</field>
+ <field name="active">False</field>
+ </record>
+</odoo>
diff --git a/indoteknik_custom/views/ir_sequence.xml b/indoteknik_custom/views/ir_sequence.xml
index 46148606..55e48300 100644
--- a/indoteknik_custom/views/ir_sequence.xml
+++ b/indoteknik_custom/views/ir_sequence.xml
@@ -260,5 +260,14 @@
<field name="number_next">1</field>
<field name="number_increment">1</field>
</record>
+
+ <record id="seq_gudang_service" model="ir.sequence">
+ <field name="name">Gudang Service</field>
+ <field name="code">gudang.service</field>
+ <field name="prefix">MGS/%(year)s/%(month)s/</field>
+ <field name="padding">4</field>
+ <field name="number_next">1</field>
+ <field name="number_increment">1</field>
+ </record>
</data>
</odoo> \ No newline at end of file
diff --git a/indoteknik_custom/views/kartu_stock.xml b/indoteknik_custom/views/kartu_stock.xml
new file mode 100644
index 00000000..705d86a2
--- /dev/null
+++ b/indoteknik_custom/views/kartu_stock.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<odoo>
+ <data>
+ <record id="kartu_stok_wizard_form_inherit_single_excel" model="ir.ui.view">
+ <field name="name">kartu.stok.wizard.form.inherit.single.excel</field>
+ <field name="model">kartu.stok.wizard</field>
+ <field name="inherit_id" ref="proweb_kartu_stok.print_kartu_stok_view_form"/>
+ <field name="arch" type="xml">
+ <xpath expr="//footer" position="inside">
+
+ <button name="action_kartu_stok_excel_single_sheet" type="object" string="Print Excel (Single Sheet)" class="btn-primary"/>
+
+ </xpath>
+ </field>
+ </record>
+ </data>
+</odoo> \ No newline at end of file