summaryrefslogtreecommitdiff
path: root/fixco_custom/models/upload_cancel_picking.py
blob: 99a15a2a620720a4e38520a2b58ddab8c9ea66c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
from odoo import models, fields, api, _
from datetime import datetime
import base64
import xlrd
from odoo.exceptions import ValidationError


class UploadCancelPicking(models.Model):
    _name = "upload.cancel.picking"
    _description = "Upload Cancel Picking"
    _order = "create_date desc"
    _rec_name = "number"

    picking_lines = fields.One2many(
        'upload.cancel.picking.line',
        'upload_cancel_picking_id',
        string='Lines',
        copy=False
    )
    number = fields.Char('Number', copy=False)
    date_upload = fields.Datetime('Cancel Date', copy=False)
    user_id = fields.Many2one(
        'res.users',
        'Created By',
        default=lambda self: self.env.user
    )
    excel_file = fields.Binary('Excel File', attachment=True)
    filename = fields.Char('File Name')

    @api.model
    def create(self, vals):
        vals['number'] = self.env['ir.sequence'].next_by_code(
            'upload.cancel.picking'
        ) or '/'
        return super().create(vals)

    def action_import_excel(self):
        self.ensure_one()

        if not self.excel_file:
            raise ValidationError(_("Please upload an Excel file first."))

        # === Load Excel ===
        try:
            file_content = base64.b64decode(self.excel_file)
            workbook = xlrd.open_workbook(file_contents=file_content)
            sheet = workbook.sheet_by_index(0)
        except Exception:
            raise ValidationError(_("Invalid Excel file format."))

        # === Validate Header ===
        header = [
            str(sheet.cell(0, col).value).strip().lower()
            for col in range(sheet.ncols)
        ]

        if 'invoice' not in header:
            raise ValidationError(
                _("Invalid Excel format. Expected column: Invoice")
            )

        invoice_col = header.index('invoice')

        # === Read Rows ===
        rows_data = []
        for row_idx in range(1, sheet.nrows):
            invoice_marketplace = str(
                sheet.cell(row_idx, invoice_col).value
            ).strip()

            if not invoice_marketplace:
                raise ValidationError(
                    _("Invoice kosong di baris Excel %s") % (row_idx + 1)
                )

            rows_data.append((row_idx + 1, invoice_marketplace))

        if not rows_data:
            raise ValidationError(_("Excel tidak berisi data."))

        # === Validate Duplicate in Excel ===
        seen = set()
        duplicate_excel_rows = []

        for row_num, invoice_marketplace in rows_data:
            if invoice_marketplace in seen:
                duplicate_excel_rows.append(str(row_num))
            seen.add(invoice_marketplace)

        if duplicate_excel_rows:
            raise ValidationError(
                _("Duplicate Invoice di file Excel pada baris: %s")
                % ", ".join(duplicate_excel_rows)
            )

        # === Validate Duplicate in System ===
        invoice_to_check = [inv for _, inv in rows_data]
        existing_invoices = set()

        chunk_size = 500
        for i in range(0, len(invoice_to_check), chunk_size):
            chunk = invoice_to_check[i:i + chunk_size]
            records = self.env['upload.cancel.picking.line'].search([
                ('invoice_marketplace', 'in', chunk)
            ])
            existing_invoices.update(records.mapped('invoice_marketplace'))

        duplicate_system_rows = []
        for row_num, invoice_marketplace in rows_data:
            if invoice_marketplace in existing_invoices:
                duplicate_system_rows.append(str(row_num))

        if duplicate_system_rows:
            raise ValidationError(
                _("Invoice Marketplace sudah ada di sistem. "
                  "Ditemukan di baris: %s")
                % ", ".join(duplicate_system_rows)
            )

        # === Create Lines ===
        line_vals = [
            (0, 0, {
                'invoice_marketplace': invoice_marketplace,
                'upload_cancel_picking_id': self.id,
            })
            for _, invoice_marketplace in rows_data
        ]

        # Clear old lines (safe way)
        self.picking_lines = [(5, 0, 0)]
        self.write({'picking_lines': line_vals})
        self.picking_lines.get_order_id()

        return {
            'type': 'ir.actions.client',
            'tag': 'display_notification',
            'params': {
                'title': _('Success'),
                'message': _('Imported %s lines from Excel.')
                           % len(line_vals),
                'sticky': False,
                'next': {'type': 'ir.actions.act_window_close'},
            }
        }

    def action_cancel_picking(self):
        self.date_upload = datetime.utcnow()
        for line in self.picking_lines:
            queue_job = self.env['queue.job'].search([('res_id', '=', line.id), ('method_name', '=', 'cancel_picking'), ('state', '!=', 'error')], limit=1)
            if queue_job:
                continue
            self.env['queue.job'].create({
                'name': f'Cancel Picking {line.picking_id.name}',
                'model_name': 'upload.cancel.picking.line',
                'method_name': 'cancel_picking',
                'res_id': line.id,
            })


class UploadCancelPickingLine(models.Model):
    _name = "upload.cancel.picking.line"
    _description = "Upload Cancel Picking Line"
    _inherit = ['mail.thread']

    upload_cancel_picking_id = fields.Many2one(
        'upload.cancel.picking',
        string='Upload'
    )
    invoice_marketplace = fields.Char(
        'Invoice Marketplace',
        required=True
    )
    picking_id = fields.Many2one(
        'stock.picking',
        'Picking Reference'
    )
    status_picking = fields.Selection([
        ('done', 'Done'),
        ('cancel', 'Cancel'),
        ('assigned', 'Ready'),
        ('confirmed', 'Waiting'),
        ('waiting', 'Waiting Another Operation'),
    ], related='picking_id.state')
    message_error = fields.Text('Error Message')
    is_grouped = fields.Boolean('Is Grouped', default=False)
    group_key = fields.Char('Group Key')


    def get_order_id(self):
        StockPicking = self.env['stock.picking']

        invoices = self.mapped('invoice_marketplace')

        if not invoices:
            return

        # Ambil semua picking yang matching invoice_mp
        pickings = StockPicking.search([
            ('invoice_mp', 'in', invoices)
        ])

        picking_map = {
            p.invoice_mp: p.id
            for p in pickings
            if p.invoice_mp
        }

        for line in self:
            picking_id = picking_map.get(line.invoice_marketplace)

            if picking_id:
                line.picking_id = picking_id
                line.message_error = False
            else:
                line.picking_id = False
                line.message_error = _(
                    "Stock Picking tidak ditemukan untuk invoice %s"
                ) % line.invoice_marketplace

    def cancel_picking(self):
        self.picking_id.action_cancel()