[UPD] update flow input hlidays ind

This commit is contained in:
Madanil
2026-10-20 14:39:32 +07:00
parent c0a185478b
commit dd44452fa1
19 changed files with 875 additions and 377 deletions

View File

@@ -3,6 +3,7 @@
Urutan import penting karena ticket bergantung pada stage_log dan conversation.
"""
from . import gmn_res_config_settings_v01
from . import gmn_freshservice_holiday_v01
from . import gmn_freshservice_ticket_stage_log_v01
from . import gmn_freshservice_ticket_conversation_v01
from . import gmn_freshservice_ticket_v01

View File

@@ -0,0 +1,276 @@
# -*- coding: utf-8 -*-
"""Model Master Data Hari Libur Nasional & Cuti Bersama.
Menyimpan daftar tanggal merah resmi Indonesia dan cuti bersama perusahaan
yang digunakan oleh Working Hours Engine untuk memotong jam kerja secara presisi.
Mendukung:
1. Input manual melalui antarmuka Tree/Form Odoo.
2. Impor massal dari file spreadsheet Excel (.xlsx).
3. Pengunduhan template spreadsheet Excel (.xlsx) dengan contoh data.
4. Pemuatan instan data baku hari libur nasional Indonesia (2024-2026).
"""
import io
import base64
import logging
from datetime import datetime, date
from odoo import models, fields, api, _
from odoo.exceptions import UserError
import xlsxwriter
_logger = logging.getLogger(__name__)
# Daftar Baku Hari Libur Nasional & Cuti Bersama Indonesia (2024-2026)
DEFAULT_INDONESIA_HOLIDAYS = [
# 2024
('2024-01-01', 'Tahun Baru 2024 Masehi', 'public', 'SKB 3 Menteri 2024'),
('2024-02-08', 'Isra Mikraj Nabi Muhammad SAW', 'public', 'SKB 3 Menteri 2024'),
('2024-02-09', 'Cuti Bersama Tahun Baru Imlek 2575 Kongzili', 'joint_leave', 'SKB 3 Menteri 2024'),
('2024-02-10', 'Tahun Baru Imlek 2575 Kongzili', 'public', 'SKB 3 Menteri 2024'),
('2024-03-11', 'Hari Suci Nyepi Tahun Baru Saka 1946', 'public', 'SKB 3 Menteri 2024'),
('2024-03-12', 'Cuti Bersama Hari Suci Nyepi', 'joint_leave', 'SKB 3 Menteri 2024'),
('2024-03-29', 'Wafat Isa Al Masih', 'public', 'SKB 3 Menteri 2024'),
('2024-03-31', 'Hari Paskah', 'public', 'SKB 3 Menteri 2024'),
('2024-04-08', 'Cuti Bersama Hari Raya Idul Fitri 1445 H', 'joint_leave', 'SKB 3 Menteri 2024'),
('2024-04-09', 'Cuti Bersama Hari Raya Idul Fitri 1445 H', 'joint_leave', 'SKB 3 Menteri 2024'),
('2024-04-10', 'Hari Raya Idul Fitri 1445 Hijriah', 'public', 'SKB 3 Menteri 2024'),
('2024-04-11', 'Hari Raya Idul Fitri 1445 Hijriah', 'public', 'SKB 3 Menteri 2024'),
('2024-04-12', 'Cuti Bersama Hari Raya Idul Fitri 1445 H', 'joint_leave', 'SKB 3 Menteri 2024'),
('2024-04-15', 'Cuti Bersama Hari Raya Idul Fitri 1445 H', 'joint_leave', 'SKB 3 Menteri 2024'),
('2024-05-01', 'Hari Buruh Internasional', 'public', 'SKB 3 Menteri 2024'),
('2024-05-09', 'Kenaikan Isa Al Masih', 'public', 'SKB 3 Menteri 2024'),
('2024-05-10', 'Cuti Bersama Kenaikan Isa Al Masih', 'joint_leave', 'SKB 3 Menteri 2024'),
('2024-05-23', 'Hari Raya Waisak 2568 BE', 'public', 'SKB 3 Menteri 2024'),
('2024-05-24', 'Cuti Bersama Hari Raya Waisak', 'joint_leave', 'SKB 3 Menteri 2024'),
('2024-06-01', 'Hari Lahir Pancasila', 'public', 'SKB 3 Menteri 2024'),
('2024-06-17', 'Hari Raya Idul Adha 1445 Hijriah', 'public', 'SKB 3 Menteri 2024'),
('2024-06-18', 'Cuti Bersama Hari Raya Idul Adha', 'joint_leave', 'SKB 3 Menteri 2024'),
('2024-07-07', 'Tahun Baru Islam 1446 Hijriah', 'public', 'SKB 3 Menteri 2024'),
('2024-08-17', 'Hari Proklamasi Kemerdekaan RI ke-79', 'public', 'SKB 3 Menteri 2024'),
('2024-09-16', 'Maulid Nabi Muhammad SAW', 'public', 'SKB 3 Menteri 2024'),
('2024-12-25', 'Hari Raya Natal', 'public', 'SKB 3 Menteri 2024'),
('2024-12-26', 'Cuti Bersama Hari Raya Natal', 'joint_leave', 'SKB 3 Menteri 2024'),
# 2025
('2025-01-01', 'Tahun Baru 2025 Masehi', 'public', 'SKB 3 Menteri 2025'),
('2025-01-27', 'Isra Mikraj Nabi Muhammad SAW', 'public', 'SKB 3 Menteri 2025'),
('2025-01-28', 'Cuti Bersama Tahun Baru Imlek 2576 Kongzili', 'joint_leave', 'SKB 3 Menteri 2025'),
('2025-01-29', 'Tahun Baru Imlek 2576 Kongzili', 'public', 'SKB 3 Menteri 2025'),
('2025-03-28', 'Cuti Bersama Hari Suci Nyepi', 'joint_leave', 'SKB 3 Menteri 2025'),
('2025-03-29', 'Hari Suci Nyepi Tahun Baru Saka 1947', 'public', 'SKB 3 Menteri 2025'),
('2025-03-31', 'Hari Raya Idul Fitri 1446 Hijriah', 'public', 'SKB 3 Menteri 2025'),
('2025-04-01', 'Hari Raya Idul Fitri 1446 Hijriah', 'public', 'SKB 3 Menteri 2025'),
('2025-04-02', 'Cuti Bersama Hari Raya Idul Fitri 1446 H', 'joint_leave', 'SKB 3 Menteri 2025'),
('2025-04-03', 'Cuti Bersama Hari Raya Idul Fitri 1446 H', 'joint_leave', 'SKB 3 Menteri 2025'),
('2025-04-04', 'Cuti Bersama Hari Raya Idul Fitri 1446 H', 'joint_leave', 'SKB 3 Menteri 2025'),
('2025-04-07', 'Cuti Bersama Hari Raya Idul Fitri 1446 H', 'joint_leave', 'SKB 3 Menteri 2025'),
('2025-04-18', 'Wafat Isa Al Masih', 'public', 'SKB 3 Menteri 2025'),
('2025-04-20', 'Hari Paskah', 'public', 'SKB 3 Menteri 2025'),
('2025-05-01', 'Hari Buruh Internasional', 'public', 'SKB 3 Menteri 2025'),
('2025-05-12', 'Hari Raya Waisak 2569 BE', 'public', 'SKB 3 Menteri 2025'),
('2025-05-13', 'Cuti Bersama Hari Raya Waisak', 'joint_leave', 'SKB 3 Menteri 2025'),
('2025-05-29', 'Kenaikan Isa Al Masih', 'public', 'SKB 3 Menteri 2025'),
('2025-05-30', 'Cuti Bersama Kenaikan Isa Al Masih', 'joint_leave', 'SKB 3 Menteri 2025'),
('2025-06-01', 'Hari Lahir Pancasila', 'public', 'SKB 3 Menteri 2025'),
('2025-06-06', 'Hari Raya Idul Adha 1446 Hijriah', 'public', 'SKB 3 Menteri 2025'),
('2025-06-09', 'Cuti Bersama Hari Raya Idul Adha', 'joint_leave', 'SKB 3 Menteri 2025'),
('2025-06-27', '1 Muharam Tahun Baru Islam 1447 H', 'public', 'SKB 3 Menteri 2025'),
('2025-08-17', 'Hari Proklamasi Kemerdekaan RI ke-80', 'public', 'SKB 3 Menteri 2025'),
('2025-09-05', 'Maulid Nabi Muhammad SAW', 'public', 'SKB 3 Menteri 2025'),
('2025-12-25', 'Hari Raya Natal', 'public', 'SKB 3 Menteri 2025'),
('2025-12-26', 'Cuti Bersama Hari Raya Natal', 'joint_leave', 'SKB 3 Menteri 2025'),
# 2026
('2026-01-01', 'Tahun Baru 2026 Masehi', 'public', 'SKB 3 Menteri 2026'),
('2026-01-16', 'Isra Mikraj Nabi Muhammad SAW', 'public', 'SKB 3 Menteri 2026'),
('2026-02-17', 'Tahun Baru Imlek 2577 Kongzili', 'public', 'SKB 3 Menteri 2026'),
('2026-03-19', 'Hari Raya Idul Fitri 1447 Hijriah', 'public', 'SKB 3 Menteri 2026'),
('2026-03-20', 'Hari Raya Idul Fitri 1447 Hijriah', 'public', 'SKB 3 Menteri 2026'),
('2026-03-21', 'Hari Suci Nyepi Tahun Baru Saka 1948', 'public', 'SKB 3 Menteri 2026'),
('2026-04-03', 'Wafat Isa Al Masih', 'public', 'SKB 3 Menteri 2026'),
('2026-05-01', 'Hari Buruh Internasional', 'public', 'SKB 3 Menteri 2026'),
('2026-05-14', 'Kenaikan Isa Al Masih', 'public', 'SKB 3 Menteri 2026'),
('2026-05-31', 'Hari Raya Waisak 2570 BE', 'public', 'SKB 3 Menteri 2026'),
('2026-06-01', 'Hari Lahir Pancasila', 'public', 'SKB 3 Menteri 2026'),
('2026-06-27', 'Hari Raya Idul Adha 1447 Hijriah', 'public', 'SKB 3 Menteri 2026'),
('2026-07-16', 'Tahun Baru Islam 1448 Hijriah', 'public', 'SKB 3 Menteri 2026'),
('2026-08-17', 'Hari Kemerdekaan Republik Indonesia ke-81', 'public', 'SKB 3 Menteri 2026'),
('2026-08-25', 'Maulid Nabi Muhammad SAW', 'public', 'SKB 3 Menteri 2026'),
('2026-12-25', 'Hari Raya Natal', 'public', 'SKB 3 Menteri 2026'),
]
class FreshserviceHoliday(models.Model):
"""Model Master Data Hari Libur Nasional & Cuti Bersama.
Menyimpan tanggal merah yang dijadikan acuan pemotongan jam kerja oleh modul.
"""
_name = 'freshservice.holiday'
_description = 'Master Data Hari Libur & Cuti Bersama Indonesia'
_order = 'date desc, id desc'
name = fields.Char(string='Nama Hari Libur / Keterangan', required=True, index=True)
date = fields.Date(string='Tanggal Libur', required=True, index=True)
holiday_type = fields.Selection([
('public', 'Hari Libur Nasional'),
('joint_leave', 'Cuti Bersama'),
('company', 'Libur Khusus Perusahaan'),
], string='Jenis Libur', default='public', required=True)
year = fields.Integer(string='Tahun', compute='_compute_year', store=True, index=True)
active = fields.Boolean(string='Aktif', default=True)
notes = fields.Text(string='Catatan Tambahan')
_sql_constraints = [
('date_unique', 'unique(date)', 'Tanggal hari libur ini sudah terdaftar dalam sistem!')
]
@api.depends('date')
def _compute_year(self):
"""Menghitung tahun secara otomatis dari tanggal libur."""
for rec in self:
rec.year = rec.date.year if rec.date else 0
def action_populate_default_holidays(self, *args, **kwargs):
"""Memuat daftar hari libur baku Indonesia (2024-2026) ke dalam database.
Hanya menambahkan data yang belum terdaftar tanpa menimpa data yang sudah ada.
"""
created_count = 0
for date_str, name, h_type, notes in DEFAULT_INDONESIA_HOLIDAYS:
existing = self.search([('date', '=', date_str)], limit=1)
if not existing:
self.create({
'date': date_str,
'name': name,
'holiday_type': h_type,
'notes': notes,
'active': True,
})
created_count += 1
message = _('Berhasil memuat %d hari libur nasional baku ke database.') % created_count if created_count > 0 else _('Seluruh hari libur nasional baku sudah terdaftar.')
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('Pemuatan Data Baku Selesai'),
'message': message,
'type': 'success',
'sticky': False,
'next': {'type': 'ir.actions.client', 'tag': 'reload'},
}
}
def action_download_template(self, *args, **kwargs):
"""Membuat dan mengunduh file template Excel (.xlsx) untuk impor hari libur.
Template dilengkapi dengan format kolom yang benar dan contoh data resmi 2026.
"""
output = io.BytesIO()
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
worksheet = workbook.add_worksheet('Template Hari Libur')
# Formatter Styling Excel
header_banner_fmt = workbook.add_format({
'bold': True, 'font_size': 14, 'font_name': 'Calibri',
'font_color': '#FFFFFF', 'bg_color': '#1B365D',
'align': 'left', 'valign': 'vcenter'
})
info_fmt = workbook.add_format({
'font_size': 9, 'font_name': 'Calibri', 'font_color': '#5D6D7E',
'italic': True, 'valign': 'vcenter'
})
th_fmt = workbook.add_format({
'bold': True, 'font_size': 10, 'font_name': 'Calibri',
'font_color': '#FFFFFF', 'bg_color': '#2E4053',
'align': 'center', 'valign': 'vcenter', 'border': 1, 'border_color': '#BDC3C7'
})
td_center_fmt = workbook.add_format({
'font_size': 9, 'font_name': 'Calibri', 'align': 'center',
'border': 1, 'border_color': '#D5D8DC', 'valign': 'vcenter'
})
td_date_fmt = workbook.add_format({
'font_size': 9, 'font_name': 'Calibri', 'align': 'center',
'border': 1, 'border_color': '#D5D8DC', 'valign': 'vcenter',
'num_format': 'yyyy-mm-dd'
})
td_left_fmt = workbook.add_format({
'font_size': 9, 'font_name': 'Calibri', 'align': 'left',
'border': 1, 'border_color': '#D5D8DC', 'valign': 'vcenter'
})
# Set Lebar Kolom
worksheet.set_column('A:A', 6)
worksheet.set_column('B:B', 16)
worksheet.set_column('C:C', 40)
worksheet.set_column('D:D', 26)
worksheet.set_column('E:E', 35)
# Header Title Banner
worksheet.merge_range('A1:E1', 'TEMPLATE IMPORT HARI LIBUR NASIONAL & CUTI BERSAMA', header_banner_fmt)
worksheet.set_row(0, 30)
# Petunjuk Pengisian
worksheet.write('A2', 'PETUNJUK PENGISIAN:', info_fmt)
worksheet.write('A3', '1. Kolom Tanggal Libur (*) dan Nama Hari Libur (*) wajib diisi. Format tanggal: YYYY-MM-DD (contoh: 2026-08-17).', info_fmt)
worksheet.write('A4', '2. Kolom Jenis Libur dapat diisi: "Hari Libur Nasional", "Cuti Bersama", atau "Libur Khusus Perusahaan".', info_fmt)
worksheet.write('A5', '3. Anda dapat mengedit, menambah, atau menghapus baris contoh di bawah ini sebelum mengimpor.', info_fmt)
# Table Headers (Baris 7)
headers = ['No', 'Tanggal Libur (*)', 'Nama Hari Libur (*)', 'Jenis Libur', 'Keterangan']
for col_idx, h in enumerate(headers):
worksheet.write(6, col_idx, h, th_fmt)
worksheet.set_row(6, 24)
# Contoh Data (Hari Libur Resmi 2026 sebagai sampel lengkap)
sample_data = [
(1, '2026-01-01', 'Tahun Baru 2026 Masehi', 'Hari Libur Nasional', 'Libur Nasional Resmi'),
(2, '2026-01-16', 'Isra Mikraj Nabi Muhammad SAW', 'Hari Libur Nasional', 'SKB 3 Menteri'),
(3, '2026-02-17', 'Tahun Baru Imlek 2577 Kongzili', 'Hari Libur Nasional', 'SKB 3 Menteri'),
(4, '2026-03-19', 'Hari Raya Idul Fitri 1447 Hijriah', 'Hari Libur Nasional', 'SKB 3 Menteri'),
(5, '2026-03-20', 'Hari Raya Idul Fitri 1447 Hijriah', 'Hari Libur Nasional', 'SKB 3 Menteri'),
(6, '2026-03-21', 'Hari Suci Nyepi Tahun Baru Saka 1948', 'Hari Libur Nasional', 'SKB 3 Menteri'),
(7, '2026-04-03', 'Wafat Isa Al Masih', 'Hari Libur Nasional', 'SKB 3 Menteri'),
(8, '2026-05-01', 'Hari Buruh Internasional', 'Hari Libur Nasional', 'Hari Buruh'),
(9, '2026-05-14', 'Kenaikan Isa Al Masih', 'Hari Libur Nasional', 'SKB 3 Menteri'),
(10, '2026-05-31', 'Hari Raya Waisak 2570 BE', 'Hari Libur Nasional', 'SKB 3 Menteri'),
(11, '2026-06-01', 'Hari Lahir Pancasila', 'Hari Libur Nasional', 'Hari Lahir Pancasila'),
(12, '2026-06-27', 'Hari Raya Idul Adha 1447 Hijriah', 'Hari Libur Nasional', 'SKB 3 Menteri'),
(13, '2026-07-16', 'Tahun Baru Islam 1448 Hijriah', 'Hari Libur Nasional', 'SKB 3 Menteri'),
(14, '2026-08-17', 'Hari Kemerdekaan Republik Indonesia ke-81', 'Hari Libur Nasional', 'Proklamasi Kemerdekaan'),
(15, '2026-08-25', 'Maulid Nabi Muhammad SAW', 'Hari Libur Nasional', 'SKB 3 Menteri'),
(16, '2026-12-25', 'Hari Raya Natal', 'Hari Libur Nasional', 'Hari Natal'),
]
row = 7
for no, dt_str, name, h_type, notes in sample_data:
worksheet.write(row, 0, no, td_center_fmt)
worksheet.write(row, 1, dt_str, td_date_fmt)
worksheet.write(row, 2, name, td_left_fmt)
worksheet.write(row, 3, h_type, td_left_fmt)
worksheet.write(row, 4, notes, td_left_fmt)
row += 1
workbook.close()
output.seek(0)
file_data = output.read()
output.close()
filename = 'Template_Hari_Libur_Indonesia.xlsx'
attachment = self.env['ir.attachment'].sudo().create({
'name': filename,
'datas': base64.b64encode(file_data),
'mimetype': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'public': True,
})
return {
'type': 'ir.actions.act_url',
'url': f'/web/content/{attachment.id}?download=true',
'target': 'new',
}
def action_open_import_wizard(self, *args, **kwargs):
"""Membuka dialog popup wizard impor hari libur dari file Excel."""
return {
'name': _('Impor Hari Libur dari Excel'),
'type': 'ir.actions.act_window',
'res_model': 'freshservice.holiday.import.wizard',
'view_mode': 'form',
'target': 'new',
'context': dict(self.env.context),
}

View File

@@ -73,20 +73,51 @@ _INDONESIA_HOLIDAYS = {
def _holiday_dates(env, start_date, end_date):
"""Return public leave dates combining Indonesian public holidays with Odoo's calendar leaves."""
res = {d for d in _INDONESIA_HOLIDAYS if start_date <= d <= end_date}
if not env or not hasattr(env, '__getitem__'):
return res
try:
leaves = env['resource.calendar.leaves'].sudo().search([
('date_from', '<', datetime.combine(end_date + timedelta(days=1), time.min)),
('date_to', '>=', datetime.combine(start_date, time.min)),
])
for leave in leaves:
if leave.date_from:
res.add(fields.Datetime.context_timestamp(env.user, leave.date_from).date())
except Exception:
pass
"""Return public leave dates combining Indonesian public holidays with Odoo's calendar leaves.
Prioritas Pengambilan Data:
1. Model Master Data `freshservice.holiday` di database (hasil input manual atau impor Excel).
2. Fallback ke konstanta `_INDONESIA_HOLIDAYS` jika tabel master data di DB masih kosong
atau jika env Odoo tidak tersedia (misal unit test standalone).
3. Menggabungkan dengan cuti dinamis dari `resource.calendar.leaves` jika tersedia.
"""
res = set()
used_db = False
if env and hasattr(env, '__getitem__'):
try:
HolidayModel = env['freshservice.holiday'].sudo()
# Cek apakah ada record hari libur aktif di database
db_records = HolidayModel.search([
('date', '>=', start_date),
('date', '<=', end_date),
('active', '=', True),
])
if db_records:
res.update(rec.date for rec in db_records)
used_db = True
elif HolidayModel.search_count([('active', '=', True)]) > 0:
# Database sudah memiliki data libur, namun tidak ada libur di rentang tanggal ini
used_db = True
except Exception:
pass
if not used_db:
# Fallback ke konstanta _INDONESIA_HOLIDAYS jika DB belum terisi / tanpa koneksi DB
res.update(d for d in _INDONESIA_HOLIDAYS if start_date <= d <= end_date)
if env and hasattr(env, '__getitem__'):
try:
leaves = env['resource.calendar.leaves'].sudo().search([
('date_from', '<', datetime.combine(end_date + timedelta(days=1), time.min)),
('date_to', '>=', datetime.combine(start_date, time.min)),
])
for leave in leaves:
if leave.date_from:
res.add(fields.Datetime.context_timestamp(env.user, leave.date_from).date())
except Exception:
pass
return res

View File

@@ -1057,16 +1057,18 @@ class FreshserviceTicket(models.Model):
return action
@api.model
def name_search(self, name='', args=None, operator='ilike', limit=100):
def name_search(self, name='', domain=None, operator='ilike', limit=100, **kwargs):
"""Override name_search: kalau user ketik nomor tiket yang belum ada di Odoo,
otomatis fetch dari Freshservice dulu baru tampilkan.
"""
if domain is None and 'args' in kwargs:
domain = kwargs.pop('args')
if name and isinstance(name, str):
ticket_ids = re.findall(r'\d+', name.strip())
for ticket_id in ticket_ids:
if not self.search_count([('external_ticket_id', '=', ticket_id)]):
self._fetch_and_create_ticket_by_external_id(ticket_id)
return super().name_search(name=name, args=args, operator=operator, limit=limit)
return super().name_search(name=name, domain=domain, operator=operator, limit=limit, **kwargs)
@api.model
def web_search_read(self, domain, specification, offset=0, limit=None, order=None, count_limit=None):