Initial commit
This commit is contained in:
commit
652688fe8b
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
venv/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.env
|
||||||
1
__init__.py
Normal file
1
__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from . import models
|
||||||
18
__manifest__.py
Normal file
18
__manifest__.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
'name': 'Purchase Export BC Format',
|
||||||
|
'version': '1.0',
|
||||||
|
'category': 'Purchases',
|
||||||
|
'summary': 'Export Purchase Orders to BC Excel format',
|
||||||
|
'author': 'Aziz',
|
||||||
|
'description': """
|
||||||
|
This module adds a button in the Purchase Order form view to export PO
|
||||||
|
into a specific BC Excel format.
|
||||||
|
""",
|
||||||
|
'depends': ['purchase'],
|
||||||
|
'data': [
|
||||||
|
'views/purchase_order_views.xml',
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'application': False,
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
}
|
||||||
1
models/__init__.py
Normal file
1
models/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from . import purchase_order
|
||||||
102
models/purchase_order.py
Normal file
102
models/purchase_order.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import base64
|
||||||
|
import io
|
||||||
|
import logging
|
||||||
|
|
||||||
|
try:
|
||||||
|
import xlsxwriter
|
||||||
|
except ImportError:
|
||||||
|
xlsxwriter = None
|
||||||
|
|
||||||
|
from odoo import models, _
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class PurchaseOrder(models.Model):
|
||||||
|
_inherit = 'purchase.order'
|
||||||
|
|
||||||
|
def action_export_bc(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if not xlsxwriter:
|
||||||
|
raise UserError(_("The Python library 'xlsxwriter' is required. Please install it."))
|
||||||
|
|
||||||
|
output = io.BytesIO()
|
||||||
|
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
|
||||||
|
|
||||||
|
header_format = workbook.add_format({
|
||||||
|
'bold': True, 'align': 'center', 'valign': 'vcenter', 'border': 1
|
||||||
|
})
|
||||||
|
|
||||||
|
headers = ["Type", "Item No.", "Description", "Qty", "Satuan"]
|
||||||
|
|
||||||
|
lines_by_category = {}
|
||||||
|
for line in self.order_line:
|
||||||
|
# Handle display_type like line_section or line_note which shouldn't be exported as items
|
||||||
|
if line.display_type in ('line_section', 'line_note'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
categ_name = line.product_id.categ_id.name or 'Uncategorized'
|
||||||
|
# Sheet names in Excel cannot exceed 31 characters
|
||||||
|
categ_name = str(categ_name)[:31]
|
||||||
|
|
||||||
|
# Excel sheet names cannot contain certain characters like :, \\, /, ?, *, [, ]
|
||||||
|
invalid_chars = [':', '\\', '/', '?', '*', '[', ']']
|
||||||
|
for char in invalid_chars:
|
||||||
|
categ_name = categ_name.replace(char, ' ')
|
||||||
|
|
||||||
|
if categ_name not in lines_by_category:
|
||||||
|
lines_by_category[categ_name] = []
|
||||||
|
lines_by_category[categ_name].append(line)
|
||||||
|
|
||||||
|
if not lines_by_category:
|
||||||
|
raise UserError(_("There are no order lines to export."))
|
||||||
|
|
||||||
|
for categ_name, lines in lines_by_category.items():
|
||||||
|
sheet = workbook.add_worksheet(categ_name)
|
||||||
|
for col_num, header in enumerate(headers):
|
||||||
|
sheet.write(0, col_num, header, header_format)
|
||||||
|
|
||||||
|
row_num = 1
|
||||||
|
for line in lines:
|
||||||
|
item_type = "Item"
|
||||||
|
|
||||||
|
# Check custom field x_studio_bc_item_id on product
|
||||||
|
if 'x_studio_bc_item_id' in line.product_id._fields and line.product_id.x_studio_bc_item_id:
|
||||||
|
item_no = line.product_id.x_studio_bc_item_id
|
||||||
|
else:
|
||||||
|
item_no = ''
|
||||||
|
|
||||||
|
description = line.product_id.name or line.name or ''
|
||||||
|
qty = line.product_qty
|
||||||
|
|
||||||
|
# Determine packaging or unit
|
||||||
|
if 'product_packaging_id' in line._fields and line.product_packaging_id:
|
||||||
|
satuan = line.product_packaging_id.name
|
||||||
|
else:
|
||||||
|
satuan = line.product_uom_id.name or ''
|
||||||
|
|
||||||
|
sheet.write(row_num, 0, item_type)
|
||||||
|
sheet.write(row_num, 1, item_no)
|
||||||
|
sheet.write(row_num, 2, description)
|
||||||
|
sheet.write(row_num, 3, qty)
|
||||||
|
sheet.write(row_num, 4, satuan)
|
||||||
|
|
||||||
|
row_num += 1
|
||||||
|
|
||||||
|
workbook.close()
|
||||||
|
output.seek(0)
|
||||||
|
|
||||||
|
attachment_name = f"PO_Export_BC_{self.name.replace('/', '_')}.xlsx"
|
||||||
|
|
||||||
|
attachment = self.env['ir.attachment'].create({
|
||||||
|
'name': attachment_name,
|
||||||
|
'type': 'binary',
|
||||||
|
'datas': base64.b64encode(output.read()),
|
||||||
|
'mimetype': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_url',
|
||||||
|
'url': f'/web/content/{attachment.id}?download=true',
|
||||||
|
'target': 'self',
|
||||||
|
}
|
||||||
13
views/purchase_order_views.xml
Normal file
13
views/purchase_order_views.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_purchase_order_form_inherit_export_bc" model="ir.ui.view">
|
||||||
|
<field name="name">purchase.order.form.inherit.export.bc</field>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
<field name="inherit_id" ref="purchase.purchase_order_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//header" position="inside">
|
||||||
|
<button name="action_export_bc" string="Export PO to BC Format" type="object" class="oe_highlight" invisible="state not in ('purchase', 'done')"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
Loading…
Reference in New Issue
Block a user