[UPD] update logic fdd tdd
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -54,9 +54,10 @@ class FreshserviceTicketBottleneckWizard(models.TransientModel):
|
||||
date_from = fields.Date(string='Dari Tanggal Dibuat')
|
||||
date_to = fields.Date(string='Sampai Tanggal Dibuat')
|
||||
as_of = fields.Datetime(
|
||||
string='Cut-off As-Of (UTC)',
|
||||
help='Jika diisi, semua durasi stage log dipotong sampai timestamp ini. '
|
||||
'Timestamp laporan ditampilkan dalam WIB (UTC+7).',
|
||||
string='Batas Waktu Cut-Off (As-Of)',
|
||||
help='Batas waktu simulasi retrospektif (opsional). '
|
||||
'Jika diisi, perhitungan durasi seluruh stage log dipotong sampai timestamp ini. '
|
||||
'Kosongkan untuk perhitungan real-time hingga saat ini.',
|
||||
)
|
||||
|
||||
status_filter = fields.Selection([
|
||||
@@ -82,6 +83,19 @@ class FreshserviceTicketBottleneckWizard(models.TransientModel):
|
||||
file_name = fields.Char(string='Nama File', readonly=True)
|
||||
bottleneck_summary_html = fields.Html(string='Ringkasan Analisis Bottleneck', readonly=True)
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
res = super().default_get(fields_list)
|
||||
active_model = self.env.context.get('active_model')
|
||||
active_id = self.env.context.get('active_id')
|
||||
if active_model == 'freshservice.ticket' and active_id:
|
||||
ticket = self.env['freshservice.ticket'].browse(active_id)
|
||||
if ticket.exists():
|
||||
res['ticket_id'] = ticket.id
|
||||
if ticket.external_ticket_id:
|
||||
res['external_ticket_id'] = ticket.external_ticket_id
|
||||
return res
|
||||
|
||||
@api.onchange('ticket_id')
|
||||
def _onchange_ticket_id(self):
|
||||
"""Kalau user pilih tiket dari dropdown, otomatis isi nomor ID Freshservice-nya."""
|
||||
@@ -376,9 +390,14 @@ class FreshserviceTicketBottleneckWizard(models.TransientModel):
|
||||
|
||||
# Header Title
|
||||
ws1.merge_range('B2:H2', f'TICKET ANALYSIS REPORT #{t.external_ticket_id}', title_fmt)
|
||||
cutoff_info = ""
|
||||
if self.as_of:
|
||||
from datetime import timedelta as _td
|
||||
as_of_wib = (self.as_of + _td(hours=7)).strftime('%d-%m-%Y %H:%M')
|
||||
cutoff_info = f" | Cut-off As-Of: {as_of_wib} WIB"
|
||||
ws1.write(
|
||||
'B3',
|
||||
f'Dicetak: {datetime.now().strftime("%d-%m-%Y %H:%M:%S")} | Dicetak oleh: {self.env.user.name}',
|
||||
f'Dicetak: {datetime.now().strftime("%d-%m-%Y %H:%M:%S")} | Dicetak oleh: {self.env.user.name}{cutoff_info}',
|
||||
subtitle_fmt
|
||||
)
|
||||
|
||||
@@ -718,7 +737,12 @@ class FreshserviceTicketBottleneckWizard(models.TransientModel):
|
||||
return str(dt)
|
||||
|
||||
row2 = 4
|
||||
for idx2, log in enumerate(t.stage_log_ids.sorted('start_date'), start=1):
|
||||
current_idx2 = 0
|
||||
for log in t.stage_log_ids.sorted('start_date'):
|
||||
start, end = self._clipped_stage_interval(log, report_as_of)
|
||||
if not start:
|
||||
continue
|
||||
current_idx2 += 1
|
||||
resp = stage_resp_map.get(log.id, self._effective_responsibility(log))
|
||||
if resp == 'internal':
|
||||
resp_label = 'Agent Active Work'
|
||||
@@ -758,9 +782,10 @@ class FreshserviceTicketBottleneckWizard(models.TransientModel):
|
||||
cause_label = dict(log._fields['delay_cause'].selection).get(log.delay_cause, log.delay_cause or 'None')
|
||||
verif_str = 'Yes' if (log.needs_verification or resp == 'needs_verification') else 'No'
|
||||
|
||||
ws2.write(row2, 1, idx2, td_center_fmt)
|
||||
ws2.write(row2, 2, _to_wib_str(log.start_date), td_center_fmt)
|
||||
ws2.write(row2, 3, _to_wib_str(log.end_date), td_center_fmt)
|
||||
effective_end = end if (self.as_of and end) else log.end_date
|
||||
ws2.write(row2, 1, current_idx2, td_center_fmt)
|
||||
ws2.write(row2, 2, _to_wib_str(start), td_center_fmt)
|
||||
ws2.write(row2, 3, _to_wib_str(effective_end), td_center_fmt)
|
||||
ws2.write(row2, 4, log.stage_name or '-', td_fmt)
|
||||
ws2.write(row2, 5, log.effective_status or log.stage_name or '-', td_fmt)
|
||||
ws2.write(row2, 6, resp_label, resp_fmt)
|
||||
@@ -796,10 +821,14 @@ class FreshserviceTicketBottleneckWizard(models.TransientModel):
|
||||
ws3.write(3, col_idx, h, th_fmt)
|
||||
|
||||
row3 = 4
|
||||
for idx3, conv in enumerate(t.conversation_ids.sorted('created_at'), start=1):
|
||||
current_idx3 = 0
|
||||
for conv in t.conversation_ids.sorted('created_at'):
|
||||
if report_as_of and conv.created_at and conv.created_at > report_as_of:
|
||||
continue
|
||||
current_idx3 += 1
|
||||
sender_type_label = 'Requester (User)' if (conv.sender_type == 'customer' or conv.incoming) else 'Agent (IT)'
|
||||
conv_wib_str = _to_wib_str(conv.created_at)
|
||||
ws3.write(row3, 1, idx3, td_center_fmt)
|
||||
ws3.write(row3, 1, current_idx3, td_center_fmt)
|
||||
ws3.write(row3, 2, conv_wib_str, td_center_fmt)
|
||||
ws3.write(row3, 3, sender_type_label, normal_badge_fmt if conv.incoming else bn_badge_fmt)
|
||||
ws3.write(row3, 4, conv.user_name or '-', td_fmt)
|
||||
|
||||
@@ -25,14 +25,14 @@
|
||||
</group>
|
||||
|
||||
<!-- MODE SINGLE TICKET -->
|
||||
<!-- <group string="Parameter Tiket Tunggal" invisible="report_type != 'single'"> -->
|
||||
<group>
|
||||
<group>
|
||||
<group string="Identitas Tiket">
|
||||
<field name="external_ticket_id" placeholder="Contoh: 992"/>
|
||||
<field name="ticket_id" options="{'no_create': True}" placeholder="Atau cari judul tiket di Odoo..."/>
|
||||
</group>
|
||||
<group invisible="True">
|
||||
<field name="auto_sync_detail"/>
|
||||
<group string="Simulasi Cut-Off & Opsi">
|
||||
<field name="as_of" placeholder="Kosongkan jika real-time s/d saat ini"/>
|
||||
<field invisible="True" name="auto_sync_detail"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
</group>
|
||||
<group>
|
||||
<field name="status_filter"/>
|
||||
<field name="as_of" string="Batas Cut-Off (As-Of)" placeholder="Kosongkan jika real-time s/d saat ini"/>
|
||||
<field name="auto_sync_detail"/>
|
||||
</group>
|
||||
<group colspan="2">
|
||||
|
||||
Reference in New Issue
Block a user