@ -0,0 +1,46 @@ |
|||
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg |
|||
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
|
|||
All In One Purchase Kit |
|||
======================= |
|||
This module combines a verity of Purchase features. By Installing this module a user can get features like Product brand, Merge sale order, Purchase Order Line View, Amount in company currency, Amount in words, Employee Purchase requisition, Product recommendation etc.. |
|||
|
|||
Configuration |
|||
============= |
|||
- No additional configuration is required |
|||
|
|||
Company |
|||
------- |
|||
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
|||
|
|||
License |
|||
------- |
|||
GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (AGPL v3) |
|||
(https://www.gnu.org/licenses/agpl-3.0-standalone.html) |
|||
|
|||
Credits |
|||
------- |
|||
Developer: (V17)Afra MP, Contact : odoo@cybrosys.com |
|||
|
|||
Contacts |
|||
-------- |
|||
* Mail Contact : odoo@cybrosys.com |
|||
* Website : https://cybrosys.com |
|||
|
|||
Bug Tracker |
|||
----------- |
|||
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. |
|||
|
|||
Maintainer |
|||
========== |
|||
.. image:: https://cybrosys.com/images/logo.png |
|||
:target: https://cybrosys.com |
|||
|
|||
This module is maintained by Cybrosys Technologies. |
|||
|
|||
For support and more information, please visit `Our Website <https://cybrosys.com/>`__ |
|||
|
|||
Further information |
|||
=================== |
|||
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,25 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from . import controllers |
|||
from . import models |
|||
from . import report |
|||
from . import wizard |
@ -0,0 +1,90 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
{ |
|||
'name': 'All In One Purchase Kit', |
|||
'version': '17.0.1.0.0', |
|||
'category': 'Purchases', |
|||
'summary': 'An integrated module offering streamlined purchase management', |
|||
'description': 'Product Brand for products, Purchase Order Line View,' |
|||
'Company Currency Total in Purchase, Employee Purchase ' |
|||
'Requisition, Purchase All In One Report Generator,' |
|||
'Previous Purchase Product Rates, Barcode scanning support' |
|||
' for Purchase, Amount in Words in Invoice for Purchase Order,' |
|||
'Multiple Purchase Order Confirm And Cancel,Merge Same ' |
|||
'Product Line, Product image in order-line,' |
|||
'Purchase discount from Purchase order line,' |
|||
'Product Recommendation', |
|||
'author': 'Cybrosys Techno Solutions', |
|||
'company': 'Cybrosys Techno Solutions', |
|||
'maintainer': 'Cybrosys Techno Solutions', |
|||
'website': 'https://www.cybrosys.com', |
|||
'depends': ['base', 'hr', 'stock', 'purchase'], |
|||
'data': [ |
|||
'security/all_in_one_purchase_kit_groups.xml', |
|||
'security/all_in_one_purchase_kit_security.xml', |
|||
'security/ir.model.access.csv', |
|||
'data/ir_sequence_data.xml', |
|||
'data/mail_template_data.xml', |
|||
'views/employee_purchase_requisition_views.xml', |
|||
'views/hr_employee_views.xml', |
|||
'views/hr_department_views.xml', |
|||
'views/purchase_order_views.xml', |
|||
'views/stock_picking_views.xml', |
|||
'views/employee_purchase_requisition_action.xml', |
|||
'views/product_brand_views.xml', |
|||
'views/product_template_views.xml', |
|||
'views/purchase_report_views.xml', |
|||
'views/purchase_order_line_views.xml', |
|||
'views/rfq_line_views.xml', |
|||
'views/purchase_report.xml', |
|||
'views/account_move_views.xml', |
|||
'views/product_product_views.xml', |
|||
'views/res_config_settings_views.xml', |
|||
'views/product_supplier_views.xml', |
|||
'views/res_partner_views.xml', |
|||
'views/purchase_dashboard.xml', |
|||
'report/purchase_order_report_templates.xml', |
|||
'report/all_in_one_purchase_kit_report_views.xml', |
|||
'report/purchase_requisition_templates.xml', |
|||
'report/purchase_order_templates.xml', |
|||
'report/dynamic_purchase_report_action.xml', |
|||
'wizard/product_recommendation_views.xml', |
|||
'views/all_in_one_purchase_kit_menus.xml', |
|||
], |
|||
'assets': { |
|||
'web.assets_backend': [ |
|||
'all_in_one_purchase_kit/static/src/js/purchase_report.js', |
|||
'all_in_one_purchase_kit/static/src/js/PurchaseDashboard.js', |
|||
'all_in_one_purchase_kit/static/src/js/purchaseTile.js', |
|||
'all_in_one_purchase_kit/static/src/css/purchase_dashboard.css', |
|||
'all_in_one_purchase_kit/static/src/xml/purchase_report_views.xml', |
|||
'all_in_one_purchase_kit/static/src/xml/dasboard_templates.xml', |
|||
'all_in_one_purchase_kit/static/src/xml/purchaseTile.xml', |
|||
'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.bundle.js' |
|||
], |
|||
}, |
|||
'images': ['static/description/banner.jpg'], |
|||
'license': 'AGPL-3', |
|||
'installable': True, |
|||
'auto_install': False, |
|||
'application': False, |
|||
} |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from . import all_in_one_purchase_kit |
@ -0,0 +1,63 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author:Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
import json |
|||
from odoo import http |
|||
from odoo.http import content_disposition, request |
|||
from odoo.tools import html_escape |
|||
|
|||
|
|||
class TBXLSXReportController(http.Controller): |
|||
""" This endpoint generates and provides an XLSX report in response to an |
|||
HTTP POST request. The function uses the provided data to create the |
|||
report and returns it as an XLSX file. """ |
|||
@http.route('/purchase_dynamic_xlsx_reports', type='http', auth='user', |
|||
methods=['POST'], csrf=False) |
|||
def get_report_xlsx(self, model, options, output_format, report_data, |
|||
report_name, dfr_data, **kw): |
|||
"""Endpoint for getting xlsx report """ |
|||
uid = request.session.uid |
|||
report_obj = request.env[model].with_user(uid) |
|||
dfr_data = dfr_data |
|||
options = options |
|||
token = 'dummy-because-api-expects-one' |
|||
try: |
|||
if output_format == 'xlsx': |
|||
response = request.make_response( |
|||
None, |
|||
headers=[ |
|||
('Content-Type', 'application/vnd.ms-excel'), |
|||
('Content-Disposition', |
|||
content_disposition(report_name + '.xlsx')) |
|||
] |
|||
) |
|||
report_obj.get_purchase_xlsx_report(options, response, |
|||
report_data, dfr_data) |
|||
response.set_cookie('fileToken', token) |
|||
return response |
|||
except Exception as e: |
|||
se = 0 |
|||
error = { |
|||
'code': 200, |
|||
'message': 'Odoo Server Error', |
|||
'data': se |
|||
} |
|||
return request.make_response(html_escape(json.dumps(error))) |
@ -0,0 +1,15 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<odoo> |
|||
<!-- Sequence for employee purchase requisition --> |
|||
<data noupdate="1"> |
|||
<record id="sequence_purchase_requisition" model="ir.sequence"> |
|||
<field name="name">Employee Purchase Requisition</field> |
|||
<field name="code">employee.purchase.requisition</field> |
|||
<field name="prefix">EPR</field> |
|||
<field name="padding">5</field> |
|||
<field eval="1" name="number_next"/> |
|||
<field eval="1" name="number_increment"/> |
|||
<field eval="False" name="company_id"/> |
|||
</record> |
|||
</data> |
|||
</odoo> |
@ -0,0 +1,62 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<odoo> |
|||
<data noupdate="1"> |
|||
<!--Function to set noupdate to False--> |
|||
<function name="write" model="ir.model.data"> |
|||
<function name="search" model="ir.model.data"> |
|||
<value eval="[('name', '=', 'email_template_edi_purchase_done'), ('module', '=', 'purchase')]"/> |
|||
</function> |
|||
<value eval="{'noupdate': False}"/> |
|||
</function> |
|||
<!--Confirmed po send mail data--> |
|||
<record id="purchase.email_template_edi_purchase_done" |
|||
model="mail.template"> |
|||
<field name="body_html" type="html"> |
|||
<div style="margin: 0px; padding: 0px;"> |
|||
<p style="margin: 0px; padding: 0px; font-size: 13px;"> |
|||
Dear |
|||
<t t-out="object.partner_id.name or ''">Brandon Freeman</t> |
|||
<t t-if="object.partner_id.parent_id"> |
|||
(<t t-out="object.partner_id.parent_id.name or ''"> |
|||
Azure Interior</t>) |
|||
</t> |
|||
<br/> |
|||
<br/> |
|||
Here is in attachment a purchase order |
|||
<strong t-out="object.name or ''">P00015</strong> |
|||
<t t-if="object.partner_ref"> |
|||
with reference: |
|||
<t t-out="object.partner_ref or ''">REF_XXX</t> |
|||
</t> |
|||
amounting in |
|||
<strong t-out="format_amount(object.amount_total, object.currency_id) or ''"> |
|||
$ 10.00 |
|||
</strong> |
|||
<span> |
|||
<strong t-out="object.number_to_words"/> |
|||
</span> |
|||
from <t t-out="object.company_id.name or ''"> |
|||
YourCompany</t>. |
|||
<br/> |
|||
<br/> |
|||
<t t-if="object.date_planned"> |
|||
The receipt is expected for <strong |
|||
t-out="format_date(object.date_planned) or ''"> |
|||
05/05/2021</strong>. |
|||
<br/> |
|||
<br/> |
|||
Could you please acknowledge the receipt of this order? |
|||
</t> |
|||
</p> |
|||
</div> |
|||
</field> |
|||
</record> |
|||
<!-- Function to set noupdate to True--> |
|||
<function name="write" model="ir.model.data"> |
|||
<function name="search" model="ir.model.data"> |
|||
<value eval="[('name', '=', 'email_template_edi_purchase_done'), ('module', '=', 'purchase')]"/> |
|||
</function> |
|||
<value eval="{'noupdate': True}"/> |
|||
</function> |
|||
</data> |
|||
</odoo> |
@ -0,0 +1,7 @@ |
|||
## Module <all_in_one_purchase_kit> |
|||
|
|||
#### 04.04.2024 |
|||
#### Version 17.0.1.0.0 |
|||
#### ADD |
|||
|
|||
- Initial Commit for All In One Purchase Kit |
@ -0,0 +1,37 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from . import account_move |
|||
from . import dynamic_purchase_report |
|||
from . import employee_purchase_requisition |
|||
from . import hr_department |
|||
from . import hr_employee |
|||
from . import product_brand |
|||
from . import product_product |
|||
from . import product_supplierinfo |
|||
from . import product_template |
|||
from . import purchase_order |
|||
from . import purchase_order_line |
|||
from . import purchase_report |
|||
from . import requisition_order |
|||
from . import res_config_settings |
|||
from . import res_partner |
|||
from . import stock_picking |
@ -0,0 +1,64 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class AccountMove(models.Model): |
|||
"""Inherit account.move to add fields and methods""" |
|||
_inherit = 'account.move' |
|||
|
|||
amount_total_company_signed = fields.Float( |
|||
string='Company Currency Total', |
|||
compute='_compute_amount_total_company_signed', |
|||
help="Total amount in company currency") |
|||
number_to_words = fields.Char( |
|||
string="Amount in Words (Total) : ", |
|||
compute='_compute_number_to_words', help="Amount in words") |
|||
|
|||
def _compute_amount_total_company_signed(self): |
|||
"""Compute the total amount in company currency for each record.""" |
|||
for amount in self: |
|||
amount.amount_total_company_signed = self.env[ |
|||
'res.currency']._compute( |
|||
amount.currency_id, amount.company_id.currency_id, |
|||
amount.amount_total) |
|||
|
|||
def _compute_number_to_words(self): |
|||
"""Compute the amount to words in Invoice for each record.""" |
|||
for rec in self: |
|||
rec.number_to_words = rec.currency_id.amount_to_text( |
|||
rec.amount_total) |
|||
|
|||
def action_post(self): |
|||
"""Override the default post action to merge order lines with the same |
|||
product and price.""" |
|||
for line in self.invoice_line_ids: |
|||
if line.id in self.invoice_line_ids.ids: |
|||
line_ids = self.invoice_line_ids.filtered( |
|||
lambda m: m.product_id.id == line.product_id.id and m. |
|||
price_unit == line.price_unit) |
|||
quantity = line_ids.mapped('quantity') |
|||
line_ids.write({'quantity': sum(quantity), |
|||
'price_unit': line.price_unit}) |
|||
line_ids[1:].unlink() |
|||
res = super(AccountMove, self).action_post() |
|||
return res |
@ -0,0 +1,429 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import api, fields, models |
|||
import io |
|||
import json |
|||
|
|||
try: |
|||
from odoo.tools.misc import xlsxwriter |
|||
except ImportError: |
|||
import xlsxwriter |
|||
|
|||
|
|||
class DynamicPurchaseReport(models.Model): |
|||
"""Model for getting dynamic purchase report """ |
|||
_name = "dynamic.purchase.report" |
|||
_description = "Dynamic Purchase Report" |
|||
|
|||
purchase_report = fields.Char(string="Purchase Report", |
|||
help='Name of the report') |
|||
date_from = fields.Datetime(string="Date From", help='Start date of report') |
|||
date_to = fields.Datetime(string="Date to", help='End date of report') |
|||
report_type = fields.Selection([ |
|||
('report_by_order', 'Report By Order'), |
|||
('report_by_order_detail', 'Report By Order Detail'), |
|||
('report_by_product', 'Report By Product'), |
|||
('report_by_categories', 'Report By Categories'), |
|||
('report_by_purchase_representative', |
|||
'Report By Purchase Representative'), |
|||
('report_by_state', 'Report By State')], default='report_by_order', |
|||
help='The order of the report') |
|||
|
|||
@api.model |
|||
def purchase_report(self, option): |
|||
"""Function for getting datas for requests """ |
|||
report_values = self.env['dynamic.purchase.report'].search( |
|||
[('id', '=', option[0])]) |
|||
data = { |
|||
'report_type': report_values.report_type, |
|||
'model': self, |
|||
} |
|||
if report_values.date_from: |
|||
data.update({ |
|||
'date_from': report_values.date_from, |
|||
}) |
|||
if report_values.date_to: |
|||
data.update({ |
|||
'date_to': report_values.date_to, |
|||
}) |
|||
filters = self.get_filter(option) |
|||
lines = self._get_report_values(data).get('PURCHASE') |
|||
return { |
|||
'name': "Purchase Orders", |
|||
'type': 'ir.actions.client', |
|||
'tag': 's_r', |
|||
'orders': data, |
|||
'filters': filters, |
|||
'report_lines': lines, |
|||
} |
|||
|
|||
def get_filter(self, option): |
|||
"""Function for get data according to order_by filter """ |
|||
data = self.get_filter_data(option) |
|||
filters = {} |
|||
if data.get('report_type') == 'report_by_order': |
|||
filters['report_type'] = 'Report By Order' |
|||
elif data.get('report_type') == 'report_by_order_detail': |
|||
filters['report_type'] = 'Report By Order Detail' |
|||
elif data.get('report_type') == 'report_by_product': |
|||
filters['report_type'] = 'Report By Product' |
|||
elif data.get('report_type') == 'report_by_categories': |
|||
filters['report_type'] = 'Report By Categories' |
|||
elif data.get('report_type') == 'report_by_purchase_representative': |
|||
filters['report_type'] = 'Report By Purchase Representative' |
|||
elif data.get('report_type') == 'report_by_state': |
|||
filters['report_type'] = 'Report By State' |
|||
else: |
|||
filters['report_type'] = 'report_by_order' |
|||
return filters |
|||
|
|||
def get_filter_data(self, option): |
|||
""" Function for get filter data in report """ |
|||
record = self.env['dynamic.purchase.report'].search([('id', '=', option[0])]) |
|||
default_filters = {} |
|||
filter_dict = { |
|||
'report_type': record.report_type, |
|||
} |
|||
filter_dict.update(default_filters) |
|||
return filter_dict |
|||
|
|||
def _get_report_sub_lines(self, data): |
|||
""" Function for get report value using sql query """ |
|||
report_sub_lines = [] |
|||
if data.get('report_type') == 'report_by_order': |
|||
query = """ select l.name,l.date_order, l.partner_id,l.amount_total, |
|||
l.notes,l.user_id,res_partner.name as partner, |
|||
res_users.partner_id as user_partner,sum(purchase_order_line.product_qty), |
|||
l.id as id,(SELECT res_partner.name as salesman FROM res_partner |
|||
WHERE res_partner.id = res_users.partner_id) from purchase_order as l |
|||
left join res_partner on l.partner_id = res_partner.id |
|||
left join res_users on l.user_id = res_users.id |
|||
left join purchase_order_line on l.id = purchase_order_line.order_id |
|||
where 1=1 """ |
|||
if data.get('date_from'): |
|||
query += """and l.date_order >= '%s' """ % data.get('date_from') |
|||
if data.get('date_to'): |
|||
query += """ and l.date_order <= '%s' """ % data.get('date_to') |
|||
query += """group by l.user_id,res_users.partner_id,res_partner.name, |
|||
l.partner_id,l.date_order,l.name,l.amount_total,l.notes,l.id""" |
|||
self._cr.execute(query) |
|||
report_by_order = self._cr.dictfetchall() |
|||
report_sub_lines.append(report_by_order) |
|||
elif data.get('report_type') == 'report_by_order_detail': |
|||
query = """ select l.name,l.date_order,l.partner_id,l.amount_total, |
|||
l.notes, l.user_id,res_partner.name as partner,res_users.partner_id |
|||
as user_partner,sum(purchase_order_line.product_qty), |
|||
purchase_order_line.name as product, purchase_order_line.price_unit, |
|||
purchase_order_line.price_subtotal,l.amount_total, |
|||
purchase_order_line.product_id,product_product.default_code, |
|||
(SELECT res_partner.name as salesman FROM res_partner WHERE |
|||
res_partner.id = res_users.partner_id)from purchase_order as l |
|||
left join res_partner on l.partner_id = res_partner.id |
|||
left join res_users on l.user_id = res_users.id |
|||
left join purchase_order_line on l.id = purchase_order_line.order_id |
|||
left join product_product on purchase_order_line.product_id = product_product.id |
|||
where 1=1 """ |
|||
if data.get('date_from'): |
|||
query += """ and l.date_order >= '%s' """ % data.get('date_from') |
|||
if data.get('date_to'): |
|||
query += """ and l.date_order <= '%s' """ % data.get('date_to') |
|||
query += """group by l.user_id,res_users.partner_id,res_partner.name, |
|||
l.partner_id,l.date_order,l.name,l.amount_total,l.notes, |
|||
purchase_order_line.name,purchase_order_line.price_unit, |
|||
purchase_order_line.price_subtotal,l.amount_total, |
|||
purchase_order_line.product_id,product_product.default_code""" |
|||
self._cr.execute(query) |
|||
report_by_order_details = self._cr.dictfetchall() |
|||
report_sub_lines.append(report_by_order_details) |
|||
elif data.get('report_type') == 'report_by_product': |
|||
query = """ select l.amount_total,sum(purchase_order_line.product_qty) |
|||
as qty, purchase_order_line.name as product, purchase_order_line.price_unit, |
|||
product_product.default_code,product_category.name from purchase_order |
|||
as l left join purchase_order_line on l.id = purchase_order_line.order_id |
|||
left join product_product on purchase_order_line.product_id = product_product.id |
|||
left join product_template on purchase_order_line.product_id = product_template.id |
|||
left join product_category on product_category.id = product_template.categ_id |
|||
where 1=1 """ |
|||
if data.get('date_from'): |
|||
query += """and l.date_order >= '%s' """ % data.get('date_from') |
|||
if data.get('date_to'): |
|||
query += """ and l.date_order <= '%s' """ % data.get('date_to') |
|||
query += """group by l.amount_total,purchase_order_line.name, |
|||
purchase_order_line.price_unit,purchase_order_line.product_id, |
|||
product_product.default_code,product_template.categ_id, |
|||
product_category.name""" |
|||
self._cr.execute(query) |
|||
report_by_product = self._cr.dictfetchall() |
|||
report_sub_lines.append(report_by_product) |
|||
elif data.get('report_type') == 'report_by_categories': |
|||
query = """ select product_category.name,sum(l.product_qty) as qty, |
|||
sum(l.price_subtotal) as amount_total from purchase_order_line as l |
|||
left join product_template on l.product_id = product_template.id |
|||
left join product_category on product_category.id = product_template.categ_id |
|||
left join purchase_order on l.order_id = purchase_order.id |
|||
where 1=1 """ |
|||
if data.get('date_from'): |
|||
query += """and pos_order.date_order >= '%s' """ % data.get( |
|||
'date_from') |
|||
if data.get('date_to'): |
|||
query += """ and pos_order.date_order <= '%s' """ % data.get( |
|||
'date_to') |
|||
query += "group by product_category.name" |
|||
self._cr.execute(query) |
|||
report_by_categories = self._cr.dictfetchall() |
|||
report_sub_lines.append(report_by_categories) |
|||
elif data.get('report_type') == 'report_by_purchase_representative': |
|||
query = """ select res_partner.name,sum(purchase_order_line.product_qty) |
|||
as qty,sum(purchase_order_line.price_subtotal) as amount,count(l.id) |
|||
as order from purchase_order as l left join res_users on |
|||
l.user_id = res_users.id left join res_partner on |
|||
res_users.partner_id = res_partner.id left join purchase_order_line |
|||
on l.id = purchase_order_line.order_id where 1=1 """ |
|||
if data.get('date_from'): |
|||
query += """ and l.date_order >= '%s' """ % data.get('date_from') |
|||
if data.get('date_to'): |
|||
query += """ and l.date_order <= '%s' """ % data.get('date_to') |
|||
query += "group by res_partner.name" |
|||
self._cr.execute(query) |
|||
report_by_purchase_representative = self._cr.dictfetchall() |
|||
report_sub_lines.append(report_by_purchase_representative) |
|||
elif data.get('report_type') == 'report_by_state': |
|||
query = """ select l.state,sum(purchase_order_line.product_qty) as |
|||
qty,sum(purchase_order_line.price_subtotal) as amount, |
|||
count(l.id) as order from purchase_order as l |
|||
left join res_users on l.user_id = res_users.id left join |
|||
res_partner on res_users.partner_id = res_partner.id |
|||
left join purchase_order_line on l.id = purchase_order_line.order_id |
|||
where 1=1 """ |
|||
if data.get('date_from'): |
|||
query += """and so.date_order >= '%s' """ % data.get('date_from') |
|||
if data.get('date_to'): |
|||
query += """ and so.date_order <= '%s' """ % data.get('date_to') |
|||
query += "group by l.state" |
|||
self._cr.execute(query) |
|||
report_by_state = self._cr.dictfetchall() |
|||
report_sub_lines.append(report_by_state) |
|||
return report_sub_lines |
|||
|
|||
def _get_report_values(self, data): |
|||
""" Get report values based on the provided data. """ |
|||
docs = data['model'] |
|||
if data.get('report_type'): |
|||
report_res = \ |
|||
self._get_report_sub_lines(data)[0] |
|||
else: |
|||
report_res = self._get_report_sub_lines(data) |
|||
return { |
|||
'doc_ids': self.ids, |
|||
'docs': docs, |
|||
'PURCHASE': report_res, |
|||
} |
|||
|
|||
def get_purchase_xlsx_report(self, data, response, report_data, dfr_data): |
|||
""" This function generates an XLSX report based on the provided data |
|||
and report type. It writes the report data to the response |
|||
object for download. |
|||
Args: |
|||
data (str): JSON data containing report filters. |
|||
response (object): The response object used to send the XLSX file. |
|||
report_data (str): JSON data containing the report data to be |
|||
included in the XLSX. |
|||
dfr_data: Data that doesn't appear to be used in this function.""" |
|||
report_data_main = json.loads(report_data) |
|||
output = io.BytesIO() |
|||
filters = json.loads(data) |
|||
workbook = xlsxwriter.Workbook(output, {'in_memory': True}) |
|||
sheet = workbook.add_worksheet() |
|||
head = workbook.add_format({'align': 'center', 'bold': True, |
|||
'font_size': '20px'}) |
|||
heading = workbook.add_format( |
|||
{'align': 'center', 'bold': True, 'font_size': '10px', |
|||
'border': 2, |
|||
'border_color': 'black'}) |
|||
txt_l = workbook.add_format( |
|||
{'font_size': '10px', 'border': 1, 'bold': True}) |
|||
sheet.merge_range('A2:H3', |
|||
'Purchase Report', |
|||
head) |
|||
if filters.get('report_type') == 'report_by_order': |
|||
sheet.merge_range('B5:D5', 'Report Type: ' + |
|||
filters.get('report_type'), txt_l) |
|||
sheet.write('A7', 'Order', heading) |
|||
sheet.write('B7', 'Date Order', heading) |
|||
sheet.write('C7', 'Customer', heading) |
|||
sheet.write('D7', 'Purchase Representative', heading) |
|||
sheet.write('E7', 'Total Qty', heading) |
|||
sheet.write('F7', 'Amount Total', heading) |
|||
lst = [] |
|||
for rec in report_data_main[0]: |
|||
lst.append(rec) |
|||
row = 6 |
|||
col = 0 |
|||
sheet.set_column(3, 0, 15) |
|||
sheet.set_column(4, 1, 15) |
|||
sheet.set_column(5, 2, 15) |
|||
sheet.set_column(6, 3, 15) |
|||
sheet.set_column(7, 4, 15) |
|||
sheet.set_column(8, 5, 15) |
|||
for rec_data in report_data_main: |
|||
row += 1 |
|||
sheet.write(row, col, rec_data['name'], txt_l) |
|||
sheet.write(row, col + 1, rec_data['date_order'], txt_l) |
|||
sheet.write(row, col + 2, rec_data['partner'], txt_l) |
|||
sheet.write(row, col + 3, rec_data['salesman'], txt_l) |
|||
sheet.write(row, col + 4, rec_data['sum'], txt_l) |
|||
sheet.write(row, col + 5, rec_data['amount_total'], txt_l) |
|||
if filters.get('report_type') == 'report_by_order_detail': |
|||
sheet.merge_range('B5:D5', 'Report Type: ' + |
|||
filters.get('report_type'), txt_l) |
|||
sheet.write('A7', 'Order', heading) |
|||
sheet.write('B7', 'Date Order', heading) |
|||
sheet.write('C7', 'Customer', heading) |
|||
sheet.write('D7', 'Purchase Representative', heading) |
|||
sheet.write('E7', 'Product Code', heading) |
|||
sheet.write('F7', 'Product Name', heading) |
|||
sheet.write('G7', 'Price unit', heading) |
|||
sheet.write('H7', 'Qty', heading) |
|||
sheet.write('I7', 'Price Total', heading) |
|||
lst = [] |
|||
for rec in report_data_main[0]: |
|||
lst.append(rec) |
|||
row = 6 |
|||
col = 0 |
|||
sheet.set_column(3, 0, 15) |
|||
sheet.set_column(4, 1, 15) |
|||
sheet.set_column(5, 2, 15) |
|||
sheet.set_column(6, 3, 15) |
|||
sheet.set_column(7, 4, 15) |
|||
sheet.set_column(8, 5, 15) |
|||
sheet.set_column(9, 6, 15) |
|||
sheet.set_column(10, 7, 15) |
|||
sheet.set_column(11, 8, 15) |
|||
sheet.set_column(12, 9, 15) |
|||
for rec_data in report_data_main: |
|||
row += 1 |
|||
sheet.write(row, col, rec_data['name'], txt_l) |
|||
sheet.write(row, col + 1, rec_data['date_order'], txt_l) |
|||
sheet.write(row, col + 2, rec_data['partner'], txt_l) |
|||
sheet.write(row, col + 3, rec_data['salesman'], txt_l) |
|||
sheet.write(row, col + 4, rec_data['default_code'], txt_l) |
|||
sheet.write(row, col + 5, rec_data['product'], txt_l) |
|||
sheet.write(row, col + 6, rec_data['price_unit'], txt_l) |
|||
sheet.write(row, col + 7, rec_data['sum'], txt_l) |
|||
sheet.write(row, col + 8, rec_data['amount_total'], txt_l) |
|||
if filters.get('report_type') == 'report_by_product': |
|||
sheet.merge_range('B5:D5', 'Report Type: ' + |
|||
filters.get('report_type'), txt_l) |
|||
sheet.write('A7', 'Category', heading) |
|||
sheet.write('B7', 'Product Code', heading) |
|||
sheet.write('C7', 'Product Name', heading) |
|||
sheet.write('D7', 'Qty', heading) |
|||
sheet.write('E7', 'Amount Total', heading) |
|||
lst = [] |
|||
for rec in report_data_main[0]: |
|||
lst.append(rec) |
|||
row = 6 |
|||
col = 0 |
|||
sheet.set_column(3, 0, 15) |
|||
sheet.set_column(4, 1, 15) |
|||
sheet.set_column(5, 2, 15) |
|||
sheet.set_column(6, 3, 15) |
|||
sheet.set_column(7, 4, 15) |
|||
for rec_data in report_data_main: |
|||
row += 1 |
|||
sheet.write(row, col, rec_data['name'], txt_l) |
|||
sheet.write(row, col + 1, rec_data['default_code'], txt_l) |
|||
sheet.write(row, col + 2, rec_data['product'], txt_l) |
|||
sheet.write(row, col + 3, rec_data['qty'], txt_l) |
|||
sheet.write(row, col + 4, rec_data['amount_total'], txt_l) |
|||
if filters.get('report_type') == 'report_by_categories': |
|||
sheet.merge_range('B5:D5', 'Report Type: ' + |
|||
filters.get('report_type'), txt_l) |
|||
sheet.write('B7', 'Category', heading) |
|||
sheet.write('C7', 'Qty', heading) |
|||
sheet.write('D7', 'Amount Total', heading) |
|||
lst = [] |
|||
for rec in report_data_main[0]: |
|||
lst.append(rec) |
|||
row = 6 |
|||
col = 1 |
|||
sheet.set_column(3, 1, 15) |
|||
sheet.set_column(4, 2, 15) |
|||
sheet.set_column(5, 3, 15) |
|||
for rec_data in report_data_main: |
|||
row += 1 |
|||
sheet.write(row, col, rec_data['name'], txt_l) |
|||
sheet.write(row, col + 1, rec_data['qty'], txt_l) |
|||
sheet.write(row, col + 2, rec_data['amount_total'], txt_l) |
|||
if filters.get('report_type') == 'report_by_purchase_representative': |
|||
sheet.merge_range('B5:D5', 'Report Type: ' + |
|||
filters.get('report_type'), txt_l) |
|||
sheet.write('A7', 'Purchase Representative', heading) |
|||
sheet.write('B7', 'Total Order', heading) |
|||
sheet.write('C7', 'Total Qty', heading) |
|||
sheet.write('D7', 'Total Amount', heading) |
|||
lst = [] |
|||
for rec in report_data_main[0]: |
|||
lst.append(rec) |
|||
row = 6 |
|||
col = 0 |
|||
sheet.set_column(3, 0, 15) |
|||
sheet.set_column(4, 1, 15) |
|||
sheet.set_column(5, 2, 15) |
|||
sheet.set_column(6, 3, 15) |
|||
for rec_data in report_data_main: |
|||
row += 1 |
|||
sheet.write(row, col, rec_data['name'], txt_l) |
|||
sheet.write(row, col + 1, rec_data['order'], txt_l) |
|||
sheet.write(row, col + 2, rec_data['qty'], txt_l) |
|||
sheet.write(row, col + 3, rec_data['amount'], txt_l) |
|||
if filters.get('report_type') == 'report_by_state': |
|||
sheet.merge_range('B5:D5', 'Report Type: ' + |
|||
filters.get('report_type'), txt_l) |
|||
sheet.write('A7', 'State', heading) |
|||
sheet.write('B7', 'Total Count', heading) |
|||
sheet.write('C7', 'Quantity', heading) |
|||
sheet.write('D7', 'Amount', heading) |
|||
lst = [] |
|||
for rec in report_data_main[0]: |
|||
lst.append(rec) |
|||
row = 6 |
|||
col = 0 |
|||
sheet.set_column(3, 0, 15) |
|||
sheet.set_column(4, 1, 15) |
|||
sheet.set_column(5, 2, 15) |
|||
sheet.set_column(6, 3, 15) |
|||
for rec_data in report_data_main: |
|||
row += 1 |
|||
if rec_data['state'] == 'draft': |
|||
sheet.write(row, col, 'Quotation', txt_l) |
|||
elif rec_data['state'] == 'sent': |
|||
sheet.write(row, col, 'Quotation Sent', txt_l) |
|||
elif rec_data['state'] == 'purchase': |
|||
sheet.write(row, col, 'Purchase Order', txt_l) |
|||
sheet.write(row, col + 1, rec_data['order'], txt_l) |
|||
sheet.write(row, col + 2, rec_data['qty'], txt_l) |
|||
sheet.write(row, col + 3, rec_data['amount'], txt_l) |
|||
workbook.close() |
|||
output.seek(0) |
|||
response.stream.write(output.read()) |
|||
output.close() |
@ -0,0 +1,260 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class EmployeePurchaseRequisition(models.Model): |
|||
""" Model for storing purchase requisition """ |
|||
_name = 'employee.purchase.requisition' |
|||
_description = 'Purchase Requisition' |
|||
_inherit = "mail.thread", "mail.activity.mixin" |
|||
|
|||
name = fields.Char( |
|||
string="Reference No", help="Reference Number", readonly=True |
|||
) |
|||
employee_id = fields.Many2one( |
|||
'hr.employee', string='Employee', required=True, |
|||
help='Choose the employee' |
|||
) |
|||
dept_id = fields.Many2one( |
|||
'hr.department', related='employee_id.department_id', store=True, |
|||
help='Choose Department', string='Department' |
|||
) |
|||
user_id = fields.Many2one( |
|||
'res.users', string='Requisition Responsible', required=True, |
|||
help='Requisition responsible user' |
|||
) |
|||
requisition_date = fields.Date( |
|||
string="Requisition Date", default=lambda self: fields.Date.today(), |
|||
help='Date of Requisition' |
|||
) |
|||
receive_date = fields.Date( |
|||
string="Received Date", readonly=True, help='Receive Date' |
|||
) |
|||
requisition_deadline = fields.Date( |
|||
string="Requisition Deadline", help="End date of Purchase requisition" |
|||
) |
|||
company_id = fields.Many2one( |
|||
'res.company', string='Company', default=lambda self: self.env.company, |
|||
help='Select the company' |
|||
) |
|||
requisition_order_ids = fields.One2many( |
|||
'requisition.order', 'requisition_product_id', required=True, |
|||
string="Requisition Order", help="Requisition order") |
|||
confirm_id = fields.Many2one( |
|||
'res.users', string='Confirmed By', default=lambda self: self.env.uid, |
|||
readonly=True, help='User who Confirmed the requisition.' |
|||
) |
|||
manager_id = fields.Many2one( |
|||
'res.users', string='Department Manager', readonly=True, |
|||
help='Department Manager' |
|||
) |
|||
requisition_head_id = fields.Many2one( |
|||
'res.users', string='Approved By', readonly=True, |
|||
help='User who approved the requisition.' |
|||
) |
|||
rejected_user_id = fields.Many2one( |
|||
'res.users', string='Rejected By', readonly=True, |
|||
help='user who rejected the requisition' |
|||
) |
|||
confirmed_date = fields.Date( |
|||
string='Confirmed Date', readonly=True, |
|||
help='Date of Requisition Confirmation' |
|||
) |
|||
department_approval_date = fields.Date( |
|||
string='Department Approval Date', readonly=True, |
|||
help='Department Approval Date' |
|||
) |
|||
approval_date = fields.Date( |
|||
string='Approved Date', readonly=True, help='Requisition Approval Date' |
|||
) |
|||
reject_date = fields.Date( |
|||
string='Rejection Date', readonly=True, |
|||
help='Requisition Rejected Date' |
|||
) |
|||
source_location_id = fields.Many2one( |
|||
'stock.location', string='Source Location', |
|||
help='Source location of requisition.' |
|||
) |
|||
destination_location_id = fields.Many2one( |
|||
'stock.location', string="Destination Location", |
|||
help='Destination location of requisition.' |
|||
) |
|||
delivery_type_id = fields.Many2one( |
|||
'stock.picking.type', string='Delivery To', help='Type of Delivery.' |
|||
) |
|||
internal_picking_id = fields.Many2one( |
|||
'stock.picking.type', string="Internal Picking", |
|||
help="Choose picking type" |
|||
) |
|||
requisition_description = fields.Text( |
|||
string="Reason For Requisition", help="Write description" |
|||
) |
|||
purchase_count = fields.Integer( |
|||
string='Purchase Count', help="Count of purchase order" |
|||
) |
|||
internal_transfer_count = fields.Integer( |
|||
string='Internal Transfer count', help="Internal transfer count" |
|||
) |
|||
state = fields.Selection( |
|||
[('new', 'New'), |
|||
('waiting_department_approval', 'Waiting Department Approval'), |
|||
('waiting_head_approval', 'Waiting Head Approval'), |
|||
('approved', 'Approved'), |
|||
('purchase_order_created', 'Purchase Order Created'), |
|||
('received', 'Received'), |
|||
('cancelled', 'Cancelled')], string="State", default='new', |
|||
copy=False, tracking=True, help="State of the record" |
|||
) |
|||
has_internal = fields.Boolean(string='Has Internal Transfer', |
|||
help="Will become true if this purchase " |
|||
"requisition has internal transfer", |
|||
compute="_compute_has_internal") |
|||
|
|||
@api.model |
|||
def create(self, vals): |
|||
"""Generate purchase requisition sequence""" |
|||
if vals.get('name', 'New') == 'New': |
|||
vals['name'] = self.env['ir.sequence'].next_by_code( |
|||
'employee.purchase.requisition') or 'New' |
|||
return super(EmployeePurchaseRequisition, self).create(vals) |
|||
|
|||
@api.depends('requisition_order_ids') |
|||
def _compute_has_internal(self): |
|||
"""Method to compute whether there is any internal transfer in the |
|||
purchase requisition""" |
|||
for rec in self: |
|||
has_internal_transfer = False |
|||
for order in rec.requisition_order_ids: |
|||
if order.requisition_type == 'internal_transfer': |
|||
has_internal_transfer = True |
|||
break |
|||
if has_internal_transfer: |
|||
rec.has_internal = True |
|||
else: |
|||
rec.has_internal = False |
|||
|
|||
def action_confirm_requisition(self): |
|||
"""Confirm purchase requisition""" |
|||
self.source_location_id = self.employee_id.department_id.department_location_id.id |
|||
self.destination_location_id = self.employee_id.employee_location_id.id |
|||
self.delivery_type_id = self.source_location_id.warehouse_id.in_type_id.id |
|||
self.internal_picking_id = self.source_location_id.warehouse_id.int_type_id.id |
|||
self.write({'state': 'waiting_department_approval'}) |
|||
self.confirm_id = self.env.uid |
|||
self.confirmed_date = fields.Date.today() |
|||
|
|||
def action_department_approval(self): |
|||
"""Approval from the department""" |
|||
self.write({'state': 'waiting_head_approval'}) |
|||
self.manager_id = self.env.uid |
|||
self.department_approval_date = fields.Date.today() |
|||
|
|||
def action_department_cancel(self): |
|||
"""Cancellation from department """ |
|||
self.write({'state': 'cancelled'}) |
|||
self.rejected_user_id = self.env.uid |
|||
self.reject_date = fields.Date.today() |
|||
|
|||
def action_head_approval(self): |
|||
"""Approval from department head""" |
|||
self.write({'state': 'approved'}) |
|||
self.requisition_head_id = self.env.uid |
|||
self.approval_date = fields.Date.today() |
|||
|
|||
def action_head_cancel(self): |
|||
"""Cancellation from department head""" |
|||
self.write({'state': 'cancelled'}) |
|||
self.rejected_user_id = self.env.uid |
|||
self.reject_date = fields.Date.today() |
|||
|
|||
def action_create_purchase_order(self): |
|||
"""Create purchase order and internal transfer""" |
|||
for rec in self.requisition_order_ids: |
|||
if rec.requisition_type == 'internal_transfer': |
|||
self.env['stock.picking'].create({ |
|||
'location_id': self.source_location_id.id, |
|||
'location_dest_id': self.destination_location_id.id, |
|||
'picking_type_id': self.internal_picking_id.id, |
|||
'requisition_order': self.name, |
|||
'move_ids_without_package': [(0, 0, { |
|||
'name': rec.product_id.name, |
|||
'product_id': rec.product_id.id, |
|||
'product_uom': rec.product_id.uom_id, |
|||
'product_uom_qty': rec.quantity, |
|||
'location_id': self.source_location_id.id, |
|||
'location_dest_id': self.destination_location_id.id, |
|||
})] |
|||
}) |
|||
else: |
|||
self.env['purchase.order'].create({ |
|||
'partner_id': self.employee_id.work_contact_id.id, |
|||
'requisition_order': self.name, |
|||
"order_line": [(0, 0, { |
|||
'product_id': rec.product_id.id, |
|||
'product_qty': rec.quantity, |
|||
})]}) |
|||
self.write({'state': 'purchase_order_created'}) |
|||
self.purchase_count = self.env['purchase.order'].search_count([ |
|||
('requisition_order', '=', self.name)]) |
|||
self.internal_transfer_count = self.env['stock.picking'].search_count([ |
|||
('requisition_order', '=', self.name)]) |
|||
|
|||
def action_receive(self): |
|||
"""Receive purchase requisition""" |
|||
self.write({'state': 'received'}) |
|||
self.receive_date = fields.Date.today() |
|||
|
|||
def get_purchase_order(self): |
|||
"""Purchase order smart button, when click on the smart button it |
|||
gives the purchase order of the user""" |
|||
self.ensure_one() |
|||
return { |
|||
'type': 'ir.actions.act_window', |
|||
'name': 'Purchase Order', |
|||
'view_mode': 'tree,form', |
|||
'res_model': 'purchase.order', |
|||
'domain': [('requisition_order', '=', self.name)], |
|||
} |
|||
|
|||
def get_internal_transfer(self): |
|||
"""Internal transfer smart button, when click on the smart button it |
|||
gives the internal transfer of the user""" |
|||
self.ensure_one() |
|||
return { |
|||
'type': 'ir.actions.act_window', |
|||
'name': 'Internal Transfers', |
|||
'view_mode': 'tree,form', |
|||
'res_model': 'stock.picking', |
|||
'domain': [('requisition_order', '=', self.name)], |
|||
} |
|||
|
|||
def action_print_report(self): |
|||
"""Print purchase requisition report""" |
|||
data = { |
|||
'employee': self.employee_id.name, |
|||
'records': self.read(), |
|||
'order_ids': self.requisition_order_ids.read(), |
|||
} |
|||
return self.env.ref( |
|||
'all_in_one_purchase_kit.report_purchase_requisition_action' |
|||
).report_action(self, data=data) |
@ -0,0 +1,32 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class HrDepartment(models.Model): |
|||
""" Inherit the hr.department model add department location field""" |
|||
_inherit = 'hr.department' |
|||
|
|||
department_location_id = fields.Many2one( |
|||
'stock.location', string='Destination Location', |
|||
help='Department location' |
|||
) |
@ -0,0 +1,33 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class HrEmployee(models.Model): |
|||
""" inherit hr.employee model add employee location field""" |
|||
|
|||
_inherit = 'hr.employee' |
|||
|
|||
employee_location_id = fields.Many2one( |
|||
'stock.location', string="Destination Location", |
|||
help='Employee location' |
|||
) |
@ -0,0 +1,44 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class ProductBrand(models.Model): |
|||
"""Model to add brand for products""" |
|||
_name = 'product.brand' |
|||
_description = 'Product Brand' |
|||
|
|||
name = fields.Char(String="Name", help="Brand name") |
|||
brand_image = fields.Binary(String="Brand Logo", help="Brand Logo") |
|||
product_ids = fields.One2many( |
|||
'product.template', 'brand_id', string="Product", help="Add product" |
|||
) |
|||
product_count = fields.Char( |
|||
String='Product Count', compute='_compute_count_products', store=True, |
|||
help="Count of Products" |
|||
) |
|||
|
|||
@api.depends('product_ids') |
|||
def _compute_count_products(self): |
|||
"""Methode to get the count of products in brand""" |
|||
for rec in self: |
|||
rec.product_count = len(rec.product_ids) |
@ -0,0 +1,83 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import api, fields, models |
|||
from odoo.exceptions import UserError |
|||
|
|||
|
|||
class ProductProduct(models.Model): |
|||
"""Inherit model and add fields and methods""" |
|||
_inherit = "product.product" |
|||
|
|||
order_partner_id = fields.Many2one( |
|||
'res.partner', string="Partner", help="Choose partner" |
|||
) |
|||
|
|||
def action_purchase_product_prices(self): |
|||
"""Display the purchase history of a product.""" |
|||
rel_view_id = self.env.ref( |
|||
'all_in_one_purchase_kit.last_product_purchase_prices_view') |
|||
if self.order_partner_id.id: |
|||
purchase_lines = self.env['purchase.order.line'].search( |
|||
[('product_id', '=', self.id), |
|||
('partner_id', '=', self.order_partner_id.id)], |
|||
order='create_date DESC').mapped('id') |
|||
else: |
|||
purchase_lines = self.env['purchase.order.line'].search( |
|||
[('product_id', '=', self.id)], |
|||
order='create_date DESC').mapped('id') |
|||
if not purchase_lines: |
|||
raise UserError("No purchase history found.!") |
|||
return { |
|||
'domain': [('id', 'in', purchase_lines)], |
|||
'views': [(rel_view_id.id, 'tree')], |
|||
'name': 'Purchase History', |
|||
'res_model': 'purchase.order.line', |
|||
'view_id': False, |
|||
'type': 'ir.actions.act_window', |
|||
} |
|||
|
|||
@api.model |
|||
def most_purchased_product(self): |
|||
"""Method returns most purchased products""" |
|||
purchased_qty = self.search_read([], ['name', 'purchased_product_qty']) |
|||
product_qty = {count['name']: count['purchased_product_qty'] for count |
|||
in purchased_qty if count['purchased_product_qty'] > 0} |
|||
sorted_qty = {key: val for key, val in sorted( |
|||
product_qty.items(), key=lambda ele: ele[1], reverse=True) |
|||
} |
|||
return {'purchased_qty': sorted_qty} |
|||
|
|||
def add_to_rfq(self): |
|||
"""When click on add to RFQ button product added to RFQ""" |
|||
order_id = self.env.context.get('order_id') |
|||
sale_order_id = self.env['purchase.order.line'].search( |
|||
[('order_id', '=', order_id)]) |
|||
lst = [rec.product_id for rec in sale_order_id] |
|||
if self in lst: |
|||
order_line_id = self.env['purchase.order.line'].search( |
|||
[('order_id', '=', order_id), ('product_id', '=', self.id)]) |
|||
order_line_id.product_uom_qty += 1 |
|||
else: |
|||
self.env['purchase.order.line'].create({ |
|||
'product_id': self.id, |
|||
'order_id': order_id, |
|||
}) |
@ -0,0 +1,42 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class ProductSupplierInfo(models.Model): |
|||
"""Inherit the model to add fields and methods""" |
|||
_inherit = "product.supplierinfo" |
|||
|
|||
discount = fields.Float(string="Discount (%)", help="Discount in %") |
|||
_sql_constraints = [ |
|||
( |
|||
"maximum_discount", |
|||
"CHECK (discount <= 100.0)", |
|||
"Discount must be lower than 100%.", |
|||
) |
|||
] |
|||
|
|||
@api.onchange("partner_id") |
|||
def _onchange_discount(self): |
|||
"""Add default discount to order line""" |
|||
for supplier in self.filtered("partner_id"): |
|||
supplier.write({'discount': supplier.partner_id.default_discount}) |
@ -0,0 +1,31 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class ProductTemplate(models.Model): |
|||
"""Add brand field in product.template""" |
|||
_inherit = 'product.template' |
|||
|
|||
brand_id = fields.Many2one( |
|||
'product.brand', string='Brand', help="Choose brand" |
|||
) |
@ -0,0 +1,630 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
import calendar |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class PurchaseOrder(models.Model): |
|||
""" Inherit purchase.order model and add fields and methods """ |
|||
_inherit = 'purchase.order' |
|||
|
|||
requisition_order = fields.Char( |
|||
string='Requisition Order', help='Requisition Order' |
|||
) |
|||
company_currency_amount = fields.Float( |
|||
string='Company Currency Total', compute='_compute_amount', |
|||
help="Total amount in company currency" |
|||
) |
|||
number_to_words = fields.Char( |
|||
string="Amount in Words (Total) : ", |
|||
compute='_compute_number_to_words', help="Total amount in words" |
|||
) |
|||
|
|||
def _compute_amount(self): |
|||
"""Total amount in company currency""" |
|||
for amount in self: |
|||
amount.company_currency_amount = self.env['res.currency']._convert(amount.amount_total, amount.company_id.currency_id) |
|||
|
|||
def _compute_number_to_words(self): |
|||
"""Compute the amount to words in Purchase Order""" |
|||
for rec in self: |
|||
rec.number_to_words = rec.currency_id.amount_to_text( |
|||
rec.amount_total) |
|||
|
|||
def action_multi_confirm(self): |
|||
"""Confirm multiple order by a single click""" |
|||
for order in self.env['purchase.order'].browse( |
|||
self.env.context.get('active_ids')).filtered( |
|||
lambda o: o.state in ['draft', 'sent']): |
|||
order.button_confirm() |
|||
|
|||
def action_multi_cancel(self): |
|||
"""Cancel multiple order by a single click""" |
|||
for order in self.env['purchase.order'].browse( |
|||
self.env.context.get('active_ids')): |
|||
order.button_cancel() |
|||
|
|||
def button_confirm(self): |
|||
"""The same order line merges when the confirmation button is |
|||
clicked""" |
|||
line_groups = {} |
|||
for line in self.order_line: |
|||
key = (line.product_id.id, line.price_unit) |
|||
line_groups.setdefault(key, []).append(line) |
|||
for lines in line_groups.values(): |
|||
if len(lines) > 1: |
|||
lines[0].product_qty = sum(line.product_qty for line in lines) |
|||
lines[0]._compute_amount() |
|||
for line in lines[1:]: |
|||
line.unlink() |
|||
res = super(PurchaseOrder, self).button_confirm() |
|||
return res |
|||
|
|||
@api.onchange('partner_id') |
|||
def _recompute_discount(self): |
|||
"""Calculate the discount""" |
|||
self.order_line.calculate_discount_percentage() |
|||
|
|||
@api.model |
|||
def get_purchase_data(self): |
|||
"""Returns data to the orm call to display the data in the tiles |
|||
:return: Dictionary with purchase data |
|||
:rtype: dict""" |
|||
orders = self.env['purchase.order'].search([('state', 'in', [ |
|||
'purchase', 'done'])]) |
|||
priority_orders = self.env['purchase.order'].search([ |
|||
('priority', '=', '1')]) |
|||
vendor = list(set(rec.partner_id for rec in orders)) |
|||
return { |
|||
'purchase_orders': len(orders), |
|||
'purchase_amount': sum(rec.amount_total for rec in orders), |
|||
'priority_orders': len(priority_orders), |
|||
'vendors': len(vendor) |
|||
} |
|||
|
|||
def get_yearly_data(self): |
|||
"""Get yearly purchase data. |
|||
:return: Dictionary with yearly purchase data |
|||
:rtype: dict""" |
|||
company = self.env.company.id |
|||
query = """ |
|||
SELECT COUNT(*) as po_count, SUM(amount_total) as po_sum |
|||
FROM purchase_order |
|||
WHERE company_id = %s |
|||
AND state IN ('purchase', 'done') |
|||
AND date_order >= date_trunc('year', now()) |
|||
""" |
|||
self.env.cr.execute(query, (company,)) |
|||
data = self.env.cr.dictfetchall() |
|||
query_priority = """ |
|||
SELECT COUNT(*) as priority_count |
|||
FROM purchase_order po |
|||
WHERE company_id = %s |
|||
AND state IN ('purchase', 'done') |
|||
AND EXTRACT(YEAR from date_order) = EXTRACT(YEAR from now()) |
|||
AND priority = '1' |
|||
""" |
|||
self.env.cr.execute(query_priority, (company,)) |
|||
priority_orders = self.env.cr.dictfetchall() |
|||
query_vendors = """ |
|||
SELECT COUNT(DISTINCT partner_id) as vendor_count |
|||
FROM purchase_order |
|||
WHERE company_id = %s |
|||
AND state IN ('purchase', 'done') |
|||
AND date_order < date_trunc('year', now()) |
|||
""" |
|||
self.env.cr.execute(query_vendors, (company,)) |
|||
result = self.env.cr.dictfetchall() |
|||
if result: |
|||
previous_vendors = result[0]['vendor_count'] |
|||
else: |
|||
previous_vendors = 0 |
|||
query_current_vendors = """ |
|||
SELECT COUNT(DISTINCT partner_id) as vendor_count |
|||
FROM purchase_order |
|||
WHERE company_id = %s |
|||
AND state IN ('purchase', 'done') |
|||
AND date_order BETWEEN date_trunc('year', now()) AND now() |
|||
""" |
|||
self.env.cr.execute(query_current_vendors, (company,)) |
|||
result = self.env.cr.dictfetchall() |
|||
if result: |
|||
current_vendors = result[0]['vendor_count'] |
|||
else: |
|||
current_vendors = 0 |
|||
new_vendors = current_vendors - previous_vendors |
|||
yearly = { |
|||
'purchase_orders': data[0]['po_count'], |
|||
'purchase_amount': data[0]['po_sum'] or 0, |
|||
'priority_orders': priority_orders[0]['priority_count'], |
|||
'vendors': new_vendors or 0, |
|||
} |
|||
return yearly |
|||
|
|||
def get_monthly_data(self): |
|||
"""Get monthly purchase data. |
|||
:return: Dictionary with monthly purchase data |
|||
:rtype: dict""" |
|||
company = self.env.company.id |
|||
query = """ |
|||
SELECT COUNT(*), SUM(amount_total) |
|||
FROM purchase_order po |
|||
WHERE company_id = %s |
|||
AND state IN ('purchase', 'done') |
|||
AND EXTRACT(YEAR from date_order) = EXTRACT(YEAR from |
|||
CURRENT_DATE) |
|||
AND EXTRACT(MONTH from date_order) = EXTRACT(MONTH from |
|||
CURRENT_DATE) |
|||
""" |
|||
self.env.cr.execute(query, (company,)) |
|||
data = self.env.cr.dictfetchall() |
|||
query = """ |
|||
SELECT COUNT(*) |
|||
FROM purchase_order po |
|||
WHERE company_id = %s |
|||
AND EXTRACT(YEAR from date_order) = EXTRACT(YEAR from |
|||
CURRENT_DATE) |
|||
AND EXTRACT(MONTH from date_order) = EXTRACT(MONTH from |
|||
CURRENT_DATE) |
|||
AND priority = '1' |
|||
""" |
|||
self.env.cr.execute(query, (company,)) |
|||
priority_orders = self.env.cr.dictfetchall() |
|||
query = """ |
|||
SELECT DISTINCT partner_id |
|||
FROM purchase_order po |
|||
WHERE company_id = %s |
|||
AND state IN ('purchase','done') |
|||
AND EXTRACT(month from date_order) < EXTRACT(month FROM |
|||
CURRENT_DATE) |
|||
""" |
|||
self.env.cr.execute(query, (company,)) |
|||
previous_vendors = self.env.cr.dictfetchall() |
|||
previous = [] |
|||
if len(previous_vendors) > 1: |
|||
previous = [rec['partner_id'] for rec in previous_vendors] |
|||
else: |
|||
previous.append(rec for rec in previous_vendors) |
|||
query = """ |
|||
SELECT DISTINCT partner_id |
|||
FROM purchase_order po |
|||
WHERE company_id = %s |
|||
AND state IN ('purchase','done') |
|||
AND EXTRACT(YEAR from date_order) = EXTRACT(YEAR FROM |
|||
CURRENT_DATE) |
|||
AND EXTRACT(month from date_order) = EXTRACT(month FROM |
|||
CURRENT_DATE) |
|||
""" |
|||
self.env.cr.execute(query, (company,)) |
|||
vendors = self.env.cr.dictfetchall() |
|||
new_vendors = [] |
|||
if vendors: |
|||
if len(vendors) > 1: |
|||
for rec in vendors: |
|||
if rec['partner_id'] not in previous: |
|||
new_vendors.append(rec['partner_id']) |
|||
else: |
|||
if vendors not in previous: |
|||
new_vendors.append(vendors[0]['partner_id']) |
|||
monthly = { |
|||
'purchase_orders': data[0]['count'], |
|||
'purchase_amount': data[0]['sum'], |
|||
'priority_orders': priority_orders[0]['count'], |
|||
'vendors': len(new_vendors), |
|||
'vendor_id': new_vendors, |
|||
} |
|||
return monthly |
|||
|
|||
def get_weekly_data(self): |
|||
"""Get weekly purchase data. |
|||
:return: Dictionary with weekly purchase data |
|||
:rtype: dict""" |
|||
company = self.env.company.id |
|||
query = """ |
|||
SELECT COUNT(*), SUM(amount_total), COUNT(CASE WHEN priority = '1' |
|||
THEN 1 ELSE NULL END) |
|||
FROM purchase_order |
|||
WHERE company_id = %s |
|||
AND state IN ('purchase', 'done') |
|||
AND EXTRACT(YEAR from date_order) = EXTRACT(YEAR from |
|||
CURRENT_DATE) |
|||
AND EXTRACT(WEEK from date_order) = EXTRACT(WEEK from |
|||
CURRENT_DATE) |
|||
""" |
|||
self.env.cr.execute(query, [company]) |
|||
data = self.env.cr.fetchone() |
|||
query = """ |
|||
SELECT DISTINCT partner_id |
|||
FROM purchase_order |
|||
WHERE company_id = %s |
|||
AND state IN ('purchase', 'done') |
|||
AND EXTRACT(WEEK from date_order) < EXTRACT(WEEK FROM |
|||
CURRENT_DATE) |
|||
""" |
|||
self.env.cr.execute(query, [company]) |
|||
previous_vendors = self.env.cr.dictfetchall() |
|||
previous = [rec['partner_id'] for rec in previous_vendors] |
|||
query = """ |
|||
SELECT DISTINCT partner_id |
|||
FROM purchase_order |
|||
WHERE company_id = %s |
|||
AND state IN ('purchase', 'done') |
|||
AND EXTRACT(YEAR from date_order) = EXTRACT(YEAR FROM |
|||
CURRENT_DATE) |
|||
AND EXTRACT(WEEK from date_order) = EXTRACT(WEEK FROM |
|||
CURRENT_DATE) |
|||
""" |
|||
self.env.cr.execute(query, [company]) |
|||
vendors = self.env.cr.dictfetchall() |
|||
new_vendors = [rec['partner_id'] for rec in vendors if rec[ |
|||
'partner_id'] not in previous] |
|||
weekly = { |
|||
'purchase_orders': data[0], |
|||
'purchase_amount': data[1], |
|||
'priority_orders': data[2], |
|||
'vendors': len(new_vendors) |
|||
} |
|||
return weekly |
|||
|
|||
def get_today_data(self): |
|||
"""Get purchase data for the current day. |
|||
:return: Dictionary with purchase data for the current day |
|||
:rtype: dict""" |
|||
company = self.env.company.id |
|||
query = """ |
|||
SELECT |
|||
COUNT(*) AS purchase_orders, |
|||
SUM(amount_total) AS purchase_amount, |
|||
COUNT(*) FILTER (WHERE priority = '1') AS priority_orders |
|||
FROM purchase_order |
|||
WHERE |
|||
company_id = %s |
|||
AND state IN ('purchase', 'done') |
|||
AND date_order::date = CURRENT_DATE |
|||
""" |
|||
self.env.cr.execute(query, (company,)) |
|||
today_data = self.env.cr.dictfetchall()[0] |
|||
query = """ |
|||
SELECT DISTINCT partner_id |
|||
FROM purchase_order |
|||
WHERE |
|||
company_id = %s |
|||
AND state IN ('purchase', 'done') |
|||
AND date_order::date = CURRENT_DATE |
|||
AND NOT EXISTS ( |
|||
SELECT 1 |
|||
FROM purchase_order |
|||
WHERE |
|||
company_id = %s |
|||
AND state IN ('purchase', 'done') |
|||
AND date_order::date < CURRENT_DATE |
|||
AND partner_id = purchase_order.partner_id)""" |
|||
self.env.cr.execute(query, (company, company)) |
|||
new_vendors = [r['partner_id'] for r in self.env.cr.dictfetchall()] |
|||
return { |
|||
'purchase_orders': today_data['purchase_orders'], |
|||
'purchase_amount': today_data['purchase_amount'], |
|||
'priority_orders': today_data['priority_orders'], |
|||
'vendors': len(new_vendors), |
|||
'vendor_id': new_vendors, |
|||
} |
|||
|
|||
@api.model |
|||
def get_select_mode_data(self, args): |
|||
"""Get data based on the selected filters |
|||
:param args: Selected filter |
|||
:type args: str |
|||
:return: Data based on the selected filter |
|||
:rtype: dict or False""" |
|||
data = { |
|||
'this_year': self.get_yearly_data, |
|||
'this_month': self.get_monthly_data, |
|||
'this_week': self.get_weekly_data, |
|||
'today': self.get_today_data, |
|||
}.get(args) |
|||
return data() if data else False |
|||
|
|||
def execute_query(self, query, args): |
|||
"""Returns quantity/count regarding the top entities |
|||
:param query: SQL query to be executed |
|||
:type query: str |
|||
:param args: Query parameters |
|||
:type args: str |
|||
:return: Query results |
|||
:rtype: list""" |
|||
self._cr.execute(query) |
|||
results = self._cr.dictfetchall() |
|||
final = [] |
|||
if args == 'top_product': |
|||
final = [[record.get('total_quantity') for record in results], |
|||
[record.get('product_name') for record in results]] |
|||
elif args == 'top_vendor': |
|||
final = [[record.get('count') for record in results], |
|||
[record.get('name') for record in results]] |
|||
elif args == 'top_rep': |
|||
final = [[record.get('count') for record in results], |
|||
[record.get('name') for record in results]] |
|||
return final |
|||
|
|||
@api.model |
|||
def get_top_chart_data(self, args): |
|||
"""Get top chart data based on the selected filter. |
|||
:param args: Selected filter |
|||
:type args: str |
|||
:return: Top chart data |
|||
:rtype: list""" |
|||
query = '' |
|||
company_id = self.env.company.id |
|||
if args == 'top_product': |
|||
query = f''' |
|||
SELECT DISTINCT(product_template.name) as product_name, |
|||
SUM(product_qty) as total_quantity |
|||
FROM purchase_order_line |
|||
INNER JOIN product_product ON |
|||
product_product.id=purchase_order_line.product_id |
|||
INNER JOIN product_template ON |
|||
product_product.product_tmpl_id = product_template.id |
|||
WHERE purchase_order_line.company_id = {company_id} |
|||
GROUP BY product_template.id |
|||
ORDER BY total_quantity DESC |
|||
LIMIT 10 |
|||
''' |
|||
elif args == 'top_vendor': |
|||
query = f''' |
|||
SELECT partner.name, COUNT(po.id) as count |
|||
FROM purchase_order po |
|||
JOIN res_partner partner ON po.partner_id = partner.id |
|||
WHERE po.company_id = {company_id} |
|||
GROUP BY partner.name |
|||
ORDER BY count DESC |
|||
LIMIT 10 |
|||
''' |
|||
elif args == 'top_rep': |
|||
query = f''' |
|||
SELECT partner.name, COUNT(po.id) as count |
|||
FROM purchase_order po |
|||
JOIN res_users users ON po.user_id = users.id |
|||
JOIN res_partner partner ON users.partner_id = partner.id |
|||
WHERE po.company_id = {company_id} |
|||
GROUP BY partner.name |
|||
ORDER BY count DESC |
|||
LIMIT 10 |
|||
''' |
|||
final = self.execute_query(query, args) |
|||
return final |
|||
|
|||
@api.model |
|||
def get_orders_by_month(self): |
|||
"""Get purchase orders grouped by month. |
|||
:return: Purchase orders by month |
|||
:rtype: dict""" |
|||
query = f"""select count(*), EXTRACT(month from date_order) as dates |
|||
from purchase_order po |
|||
where company_id = {self.env.company.id} and state = 'purchase' |
|||
group by dates""" |
|||
self.env.cr.execute(query, (self.env.company.id,)) |
|||
cr = self.env.cr.dictfetchall() |
|||
month = [] |
|||
for rec in cr: |
|||
month.append(int(rec['dates'])) |
|||
rec.update({ |
|||
'count': rec['count'], |
|||
'dates': calendar.month_name[int(rec['dates'])], |
|||
'month': int(rec['dates']) |
|||
}) |
|||
for rec in range(1, 13): |
|||
if rec not in month: |
|||
cr.append({ |
|||
'count': 0, |
|||
'dates': calendar.month_name[rec], |
|||
'month': rec |
|||
}) |
|||
cr = sorted(cr, key=lambda i: i['month']) |
|||
return { |
|||
'count': [rec['count'] for rec in cr], |
|||
'dates': [rec['dates'] for rec in cr] |
|||
} |
|||
|
|||
@api.model |
|||
def purchase_vendors(self): |
|||
"""Get a list of purchase vendors. |
|||
:return: List of purchase vendors in the format |
|||
[{'id': vendor_id, 'name': vendor_name}] |
|||
:rtype: list""" |
|||
company_id = self.env.company.id |
|||
query = """ |
|||
SELECT partner.id, partner.name |
|||
FROM purchase_order po |
|||
INNER JOIN res_partner partner ON po.partner_id = partner.id |
|||
WHERE po.company_id = %s |
|||
GROUP BY partner.id |
|||
""" |
|||
self._cr.execute(query, (company_id,)) |
|||
return self._cr.dictfetchall() |
|||
|
|||
@api.model |
|||
def purchase_vendor_details(self, args): |
|||
"""Get purchase details for a specific vendor. |
|||
:param args: Vendor ID |
|||
:type args: int |
|||
:return: Purchase details for the specific vendor |
|||
:rtype: dict""" |
|||
company_id = self.env.company.id |
|||
partner = int(args) if args else 1 |
|||
query = """ |
|||
SELECT count(po.id),SUM(po.amount_total), EXTRACT(MONTH from |
|||
po.date_order) as dates |
|||
FROM purchase_order po |
|||
JOIN res_partner ON res_partner.id = po.partner_id |
|||
WHERE po.company_id = %s and po.partner_id = %s |
|||
GROUP BY dates |
|||
""" |
|||
self._cr.execute(query, (company_id, partner)) |
|||
partner_orders = self._cr.dictfetchall() |
|||
query_draft = """ |
|||
SELECT count(po.id),SUM(po.amount_total), EXTRACT(MONTH from |
|||
po.date_order) as dates |
|||
FROM purchase_order po |
|||
JOIN res_partner ON res_partner.id = po.partner_id |
|||
WHERE po.state in ('draft', 'sent') and po.company_id = %s and |
|||
po.partner_id = %s |
|||
GROUP BY dates""" |
|||
self._cr.execute(query_draft, (company_id, partner)) |
|||
draft_orders = self._cr.dictfetchall() |
|||
approve_qry = """ |
|||
SELECT count(po.id),SUM(po.amount_total), EXTRACT(MONTH from |
|||
po.date_order) as dates |
|||
FROM purchase_order po |
|||
JOIN res_partner ON res_partner.id = po.partner_id |
|||
WHERE po.state = 'to approve' and po.company_id = %s and |
|||
po.partner_id = %s |
|||
GROUP BY dates""" |
|||
self._cr.execute(approve_qry, (company_id, partner)) |
|||
approve_orders = self._cr.dictfetchall() |
|||
cancel_qry = """ |
|||
SELECT count(po.id),SUM(po.amount_total), EXTRACT(MONTH from |
|||
po.date_order) as dates |
|||
FROM purchase_order po |
|||
JOIN res_partner ON res_partner.id = po.partner_id |
|||
WHERE po.state = 'cancel' and po.company_id = %s and po.partner_id |
|||
= %s |
|||
GROUP BY dates""" |
|||
self._cr.execute(cancel_qry, (company_id, partner)) |
|||
cancel_orders = self._cr.dictfetchall() |
|||
all_orders = { |
|||
'partner_orders': partner_orders, 'draft_orders': draft_orders, |
|||
'approve_orders': approve_orders, 'cancel_orders': cancel_orders} |
|||
for order_type, order_list in all_orders.items(): |
|||
order_months = [] |
|||
for rec in order_list: |
|||
order_months.append(int(rec.get('dates'))) |
|||
for rec in range(1, 13): |
|||
if rec not in order_months: |
|||
vals = {'sum': 0.0, 'dates': rec, 'count': 0} |
|||
order_list.append(vals) |
|||
all_orders[order_type] = sorted( |
|||
order_list, key=lambda order: order['dates']) |
|||
value = { |
|||
'purchase_amount': [record.get('sum') for record in |
|||
partner_orders], |
|||
'po_count': [record.get('count') for record in partner_orders], |
|||
'draft_amount': [record.get('sum') for record in draft_orders], |
|||
'draft_count': [record.get('count') for record in draft_orders], |
|||
'approve_amount': [record.get('sum') for record in approve_orders], |
|||
'approve_count': [record.get('count') for record in |
|||
approve_orders], |
|||
'cancel_amount': [record.get('sum') for record in cancel_orders], |
|||
'cancel_count': [record.get('count') for record in cancel_orders], |
|||
'dates': [record.get('dates') for record in partner_orders], |
|||
} |
|||
return value |
|||
|
|||
@api.model |
|||
def get_pending_purchase_data(self): |
|||
"""Get pending purchase orders data. |
|||
:return: Data of pending purchase orders |
|||
:rtype: dict""" |
|||
company = self.env.company.id |
|||
query = """ |
|||
SELECT po.name, po.id, rp.name as partner_name, po.date_planned, |
|||
po.amount_total, po.state |
|||
FROM purchase_order po |
|||
JOIN purchase_order_line pol ON pol.order_id = po.id |
|||
JOIN res_partner rp ON rp.id = po.partner_id |
|||
WHERE po.date_planned < CURRENT_DATE AND pol.qty_received < |
|||
pol.product_qty AND po.company_id = %s |
|||
GROUP BY po.id, rp.id |
|||
""" |
|||
self._cr.execute(query, (company,)) |
|||
orders = self._cr.dictfetchall() |
|||
value = { |
|||
'order': [rec['name'] for rec in orders], |
|||
'vendor': [rec['partner_name'] for rec in orders], |
|||
'amount': [rec['amount_total'] for rec in orders], |
|||
'date': [rec['date_planned'] for rec in orders], |
|||
'state': [rec['state'] for rec in orders], |
|||
'data': [list(val for val in rec.values()) for rec in orders] |
|||
} |
|||
return value |
|||
|
|||
@api.model |
|||
def get_upcoming_purchase_data(self): |
|||
"""Get upcoming purchase orders data. |
|||
:return: Data of upcoming purchase orders |
|||
:rtype: dict""" |
|||
company = self.env.company.id |
|||
query = """ |
|||
SELECT po.name, po.id, rp.name as partner_name, po.date_planned, |
|||
po.amount_total, po.state |
|||
FROM purchase_order po |
|||
JOIN purchase_order_line pol ON pol.order_id = po.id |
|||
JOIN res_partner rp ON rp.id = po.partner_id |
|||
WHERE po.date_planned > CURRENT_DATE AND pol.qty_received < |
|||
pol.product_qty AND po.company_id = %s |
|||
GROUP BY po.id, rp.id |
|||
""" |
|||
self._cr.execute(query, (company,)) |
|||
orders = self._cr.dictfetchall() |
|||
value = { |
|||
'order': [rec['name'] for rec in orders], |
|||
'vendor': [rec['partner_name'] for rec in orders], |
|||
'amount': [rec['amount_total'] for rec in orders], |
|||
'date': [rec['date_planned'] for rec in orders], |
|||
'state': [rec['state'] for rec in orders], |
|||
'data': [list(val for val in rec.values()) for rec in orders], |
|||
} |
|||
return value |
|||
|
|||
@api.model |
|||
def total_amount_spend(self): |
|||
"""Returns total amount spend for purchase""" |
|||
total_amount = sum(self.search([ |
|||
('state', '=', 'purchase')]).mapped('amount_total')) |
|||
return { |
|||
'amount_total': total_amount |
|||
} |
|||
|
|||
def recommendation_wizard(self): |
|||
"""Add data to wizard""" |
|||
orders = self.search([('partner_id', '=', self.partner_id.id)]) |
|||
pro_id = [] |
|||
for order in orders: |
|||
for product in order.order_line.product_id: |
|||
val = (0, 0, { |
|||
'product_id': product.id, |
|||
'available_qty': product.qty_available, |
|||
'list_price': product.list_price, |
|||
'qty_need': 0, |
|||
'is_modified': False, |
|||
}) |
|||
if val not in pro_id: |
|||
pro_id.append(val) |
|||
res = { |
|||
'type': 'ir.actions.act_window', |
|||
'view_mode': 'form', |
|||
'res_model': 'product.recommendation', |
|||
'target': 'new', |
|||
'context': { |
|||
'default_line_ids': pro_id, |
|||
} |
|||
} |
|||
return res |
@ -0,0 +1,210 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import api, fields, models, _ |
|||
from odoo.exceptions import UserError |
|||
|
|||
|
|||
class PurchaseOrderLine(models.Model): |
|||
"""Inherit purchase.order.line to add fields and methods""" |
|||
_inherit = 'purchase.order.line' |
|||
|
|||
product_image = fields.Binary( |
|||
related="product_id.image_1920", |
|||
string="Product Image", |
|||
help='For getting product image to purchase order line') |
|||
purchase_date = fields.Datetime( |
|||
comodel_name='purchase.order', related='order_id.date_order', store=True, |
|||
string='Purchase Date', help="Purchase Date" |
|||
) |
|||
barcode_scan = fields.Char( |
|||
string='Product Barcode', |
|||
help="Here you can provide the barcode for the product") |
|||
discount = fields.Float( |
|||
string="Discount (%)", editable=True, help="Total Discount" |
|||
) |
|||
_sql_constraints = [ |
|||
( |
|||
"maximum_discount", |
|||
"CHECK (discount <= 100.0)", |
|||
"Discount must be lower than 100%.", |
|||
) |
|||
] |
|||
|
|||
@api.onchange('order_id') |
|||
def _onchange_order_id(self): |
|||
""" Restrict creating purchase order line for purchase order |
|||
in locked, cancel and purchase order states""" |
|||
if self.order_id.state in ['cancel', 'done', 'purchase']: |
|||
raise UserError(_("You cannot select purchase order in " |
|||
"cancel or locked or purchase order state")) |
|||
|
|||
def get_product_form(self): |
|||
"""Get the product form""" |
|||
self.product_id.order_partner_id = self.order_id.partner_id.id |
|||
return { |
|||
'name': self.product_id.name, |
|||
'view_mode': 'form', |
|||
'res_model': 'product.product', |
|||
'type': 'ir.actions.act_window', |
|||
'target': 'current', |
|||
'res_id': self.product_id.id |
|||
} |
|||
|
|||
@api.onchange('barcode_scan') |
|||
def _onchange_barcode_scan(self): |
|||
"""Search the product with the barcode entered""" |
|||
if self.barcode_scan: |
|||
product = self.env['product.product'].search([ |
|||
('barcode', '=', self.barcode_scan)]) |
|||
self.product_id = product.id |
|||
|
|||
@api.depends("discount") |
|||
def _compute_amount(self): |
|||
"""Add discount""" |
|||
return super()._compute_amount() |
|||
|
|||
def _convert_to_tax_base_line_dict(self): |
|||
"""Update price unit""" |
|||
vals = super()._convert_to_tax_base_line_dict() |
|||
vals.update({"price_unit": self._get_discounted_price()}) |
|||
return vals |
|||
|
|||
@api.onchange('product_id') |
|||
def calculate_discount_percentage(self): |
|||
"""Calculate the discount percentage""" |
|||
vendor = self.order_id.partner_id |
|||
sellers = self.product_id.product_tmpl_id.seller_ids |
|||
for rec in sellers: |
|||
if rec.partner_id.id == vendor.id: |
|||
if rec.discount: |
|||
self.write({'discount': rec.discount}) |
|||
self.update({'price_unit': rec.price}) |
|||
break |
|||
elif rec.partner_id.id != vendor.id: |
|||
self.update({'discount': vendor.default_discount}) |
|||
break |
|||
else: |
|||
self.write({'discount': None}) |
|||
|
|||
@api.depends('discount') |
|||
def _get_discounted_price(self): |
|||
"""Returns discounted price""" |
|||
self.ensure_one() |
|||
if self.discount: |
|||
return self.price_unit * (1 - self.discount / 100) |
|||
return self.price_unit |
|||
|
|||
def _prepare_account_move_line(self, move=False): |
|||
"""Discount in account.move.line""" |
|||
sup = super(PurchaseOrderLine, self)._prepare_account_move_line(move) |
|||
sup.update({'discount': self.discount}) |
|||
return sup |
|||
|
|||
def add_catalog_control(self): |
|||
"""Method to call product.product model when click on catalog |
|||
button in purchase order line""" |
|||
return { |
|||
'type': 'ir.actions.act_window', |
|||
'name': _('Products'), |
|||
'context': {'order_id': self.env.context.get('id')}, |
|||
'res_model': 'product.product', |
|||
'view_mode': 'kanban,tree,form', |
|||
'target': 'current', |
|||
} |
|||
|
|||
def action_purchase_order(self): |
|||
"""Method action_purchase_order to return the form view of the |
|||
model purchase.order""" |
|||
return { |
|||
'type': 'ir.actions.act_window', |
|||
'name': _('Purchase Order'), |
|||
'res_id': self.order_id.id, |
|||
'res_model': 'purchase.order', |
|||
'view_mode': 'form', |
|||
'target': 'current', |
|||
} |
|||
|
|||
@api.model |
|||
def product_categ_analysis(self): |
|||
"""Get product category analysis data. |
|||
:return: Product category analysis data |
|||
:rtype: dict""" |
|||
company_id = self.env.user.company_id.id |
|||
quantity_query = """ |
|||
SELECT product_template.name, SUM(pl.product_qty) as total_quantity |
|||
FROM purchase_order_line pl |
|||
JOIN product_product ON pl.product_id = product_product.id |
|||
JOIN product_template ON product_product.product_tmpl_id = |
|||
product_template.id |
|||
WHERE pl.company_id = %s |
|||
GROUP BY product_template.name |
|||
""" |
|||
self._cr.execute(quantity_query, (company_id,)) |
|||
products_quantity = self._cr.fetchall() |
|||
if not products_quantity: # Check if products_quantity is empty |
|||
return {'values': {'name': [], 'count': []}, 'category_id': []} |
|||
name, quantity_done = zip(*products_quantity) |
|||
categories = self.get_categories() |
|||
value = {'name': name, 'count': quantity_done} |
|||
return {'values': value, 'category_id': categories} |
|||
|
|||
def get_categories(self): |
|||
"""Get product categories. |
|||
:return: Product categories |
|||
:rtype: list""" |
|||
category_query = """ |
|||
SELECT pc.id, pc.name |
|||
FROM product_category pc |
|||
JOIN product_template pt ON pt.categ_id = pc.id |
|||
JOIN product_product pp ON pp.product_tmpl_id = pt.id |
|||
JOIN purchase_order_line pl ON pl.product_id = pp.id |
|||
WHERE pl.company_id = %s |
|||
GROUP BY pc.id, pc.name |
|||
""" |
|||
self._cr.execute(category_query, (self.env.user.company_id.id,)) |
|||
return self._cr.fetchall() |
|||
|
|||
@api.model |
|||
def product_categ_data(self, args): |
|||
"""Get product category data. |
|||
:param args: Category ID |
|||
:type args: int |
|||
:return: Product category data |
|||
:rtype: dict""" |
|||
category_id = int(args or 1) |
|||
company_id = self.env.company.id |
|||
query = """ |
|||
SELECT product_template.name, SUM(pl.product_qty) |
|||
FROM purchase_order_line pl |
|||
INNER JOIN product_product ON pl.product_id = product_product.id |
|||
INNER JOIN product_template ON product_product.product_tmpl_id = |
|||
product_template.id |
|||
WHERE pl.company_id = %s AND product_template.categ_id = %s |
|||
GROUP BY product_template.name |
|||
""" |
|||
self._cr.execute(query, (company_id, category_id)) |
|||
product_move = self._cr.dictfetchall() |
|||
value = { |
|||
'name': [record.get('name') for record in product_move], |
|||
'count': [record.get('sum') for record in product_move], |
|||
} |
|||
return value |
@ -0,0 +1,46 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class PurchaseReport(models.Model): |
|||
"""Inherit model to add fields and methods""" |
|||
_inherit = 'purchase.report' |
|||
|
|||
brand_id = fields.Many2one( |
|||
'product.brand', string='Brand', help='Select brand of the product' |
|||
) |
|||
|
|||
def _select(self): |
|||
"""Add filter in pivot view""" |
|||
res = super(PurchaseReport, self)._select() |
|||
query = res.split('t.categ_id as category_id,', 1) |
|||
res = query[0] + 't.categ_id as category_id,t.brand_id' \ |
|||
' as brand_id,' + query[1] |
|||
return res |
|||
|
|||
def _group_by(self): |
|||
"""Add the group by in pivot view""" |
|||
res = super(PurchaseReport, self)._group_by() |
|||
query = res.split('t.categ_id,', 1) |
|||
res = query[0] + 't.categ_id,t.brand_id,' + query[1] |
|||
return res |
@ -0,0 +1,74 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class RequisitionOrder(models.Model): |
|||
"""Model for requisition order""" |
|||
_name = 'requisition.order' |
|||
_description = 'Requisition order' |
|||
|
|||
requisition_product_id = fields.Many2one( |
|||
'employee.purchase.requisition', string="Requisition Product", |
|||
help='Requisition product.') |
|||
state = fields.Selection( |
|||
string='State', related='requisition_product_id.state', |
|||
help="Requisition State") |
|||
requisition_type = fields.Selection( |
|||
string='Requisition Type', |
|||
selection=[ |
|||
('purchase_order', 'Purchase Order'), |
|||
('internal_transfer', 'Internal Transfer')], |
|||
help='Type of requisition') |
|||
product_id = fields.Many2one( |
|||
'product.product', required=True, string="Product", |
|||
help='Select Product') |
|||
description = fields.Text( |
|||
string="Description", |
|||
compute='_compute_product_id', |
|||
store=True, readonly=False, |
|||
precompute=True, help='Product Description') |
|||
quantity = fields.Integer(string='Quantity', help='Quantity') |
|||
uom = fields.Char( |
|||
related='product_id.uom_id.name', string='Unit of Measure', |
|||
help='Product Uom') |
|||
partner_ids = fields.Many2many('res.partner', |
|||
compute='_compute_requisition_type') |
|||
partner_id = fields.Many2one( |
|||
'res.partner', string='Vendor', |
|||
help='Vendor for the requisition', readonly=False, ) |
|||
|
|||
@api.depends('product_id') |
|||
def _compute_product_id(self): |
|||
"""Compute product description '[("id", "in", [13, 66, 65, 51] )]'""" |
|||
for option in self: |
|||
if not option.product_id: |
|||
continue |
|||
product_lang = option.product_id.with_context( |
|||
lang=self.requisition_product_id.employee_id.lang) |
|||
option.description = product_lang.get_product_multiline_description_sale() |
|||
|
|||
@api.depends('requisition_type', 'product_id') |
|||
def _compute_requisition_type(self): |
|||
"""Fetching product vendors""" |
|||
self.partner_ids = [data.partner_id.id for data in |
|||
self.product_id.seller_ids] |
@ -0,0 +1,32 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class ResConfigSettings(models.TransientModel): |
|||
"""Add field in res.config.settings""" |
|||
_inherit = 'res.config.settings' |
|||
|
|||
show_product_image_in_report_purchase = fields.Boolean( |
|||
string="Show Product Image", |
|||
config_parameter='all_in_one_purchase_kit.show_product_image_in_report_purchase', |
|||
help="Enable this field to Print image in report") |
@ -0,0 +1,50 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class ResPartner(models.Model): |
|||
"""Inheriting res partner to add fields and methods""" |
|||
_inherit = 'res.partner' |
|||
|
|||
default_discount = fields.Float(string='Discount(%)', |
|||
help="Enter discount amount in %") |
|||
_sql_constraints = [ |
|||
( |
|||
"maximum_discount", |
|||
"CHECK (discount <= 100.0)", |
|||
"Discount must be lower than 100%.", |
|||
) |
|||
] |
|||
|
|||
@api.model |
|||
def get_vendor_po(self): |
|||
"""Get purchase order of vendors""" |
|||
count_dict = { |
|||
count['name']: count['purchase_order_count'] |
|||
for count in self.search_read( |
|||
[], ['name', 'purchase_order_count'], |
|||
order='purchase_order_count') |
|||
if count['purchase_order_count'] > 0 |
|||
} |
|||
return {'purchase_order_count': {key: val for key, val in sorted( |
|||
count_dict.items(), key=lambda ele: ele[1], reverse=True)}} |
@ -0,0 +1,34 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class StockPicking(models.Model): |
|||
""" |
|||
Inherit stock.picking model and add field requisition order in |
|||
stock picking |
|||
""" |
|||
_inherit = 'stock.picking' |
|||
|
|||
requisition_order = fields.Char( |
|||
string='Requisition Order', help='Requisition Order' |
|||
) |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from . import purchase_order_report |
@ -0,0 +1,11 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<!--Purchase requisition report--> |
|||
<record id="report_purchase_requisition_action" model="ir.actions.report"> |
|||
<field name="name">Material Purchase Requisition Report</field> |
|||
<field name="model">employee.purchase.requisition</field> |
|||
<field name="report_type">qweb-pdf</field> |
|||
<field name="report_name">all_in_one_purchase_kit.report_purchase_requisition</field> |
|||
<field name="report_file">all_in_one_purchase_kit.report_purchase_requisition</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,11 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<odoo> |
|||
<!--Print pdf report--> |
|||
<record id="purchase_all_in_one_purchase_kit_action" model="ir.actions.report"> |
|||
<field name="name">Purchase All In One Report</field> |
|||
<field name="model">dynamic.purchase.report</field> |
|||
<field name="report_type">qweb-pdf</field> |
|||
<field name="report_name">all_in_one_purchase_kit.purchase_order_report</field> |
|||
<field name="report_file">all_in_one_purchase_kit.purchase_order_report</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,40 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import api, models |
|||
|
|||
|
|||
class PurchaseOrderReport(models.AbstractModel): |
|||
"""Model for creating pdf report and data fetching """ |
|||
_name = 'report.all_in_one_purchase_kit.purchase_order_report' |
|||
_description = "Purchase Report" |
|||
|
|||
@api.model |
|||
def _get_report_values(self, docids, data=None): |
|||
"""Function for get pdf report values""" |
|||
if self.env.context.get('purchase_order_report'): |
|||
if data.get('report_data'): |
|||
data.update({'report_main_line_data': data.get('report_data')[ |
|||
'report_lines'], |
|||
'Filters': data.get('report_data')['filters'], |
|||
'company': self.env.company, |
|||
}) |
|||
return data |
@ -0,0 +1,406 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<odoo> |
|||
<!--Inherit template report_purchaseorder_document--> |
|||
<record id="action_report_purchase_all_in_one" model="ir.actions.report"> |
|||
<field name="name">Purchase All In One Report</field> |
|||
<field name="model">dynamic.purchase.report</field> |
|||
<field name="report_type">qweb-pdf</field> |
|||
<field name="report_name">all_in_one_purchase_kit.purchase_order_report</field> |
|||
<field name="report_file">all_in_one_purchase_kit.purchase_order_report</field> |
|||
</record> |
|||
<!--Template to show purchase report respect to the filter selected--> |
|||
<template id="purchase_order_report"> |
|||
<t t-call="web.html_container"> |
|||
<t t-call="web.internal_layout"> |
|||
<t t-if="Filters.get('report_type')=='Report By Order'"> |
|||
<t t-call="all_in_one_purchase_kit.report_order"/> |
|||
</t> |
|||
<t t-if="Filters.get('report_type')=='Report By Order Detail'"> |
|||
<t t-call="all_in_one_purchase_kit.report_order_detail"/> |
|||
</t> |
|||
<t t-if="Filters.get('report_type')=='Report By Product'"> |
|||
<t t-call="all_in_one_purchase_kit.report_product"/> |
|||
</t> |
|||
<t t-if="Filters.get('report_type')=='Report By Categories'"> |
|||
<t t-call="all_in_one_purchase_kit.report_category"/> |
|||
</t> |
|||
<t t-if="Filters.get('report_type')=='Report By Purchase Representative'"> |
|||
<t t-call="all_in_one_purchase_kit.report_purchase_representative"/> |
|||
</t> |
|||
<t t-if="Filters.get('report_type')=='Report By State'"> |
|||
<t t-call="all_in_one_purchase_kit.report_state"/> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
</template> |
|||
<!--Report template when 'Report By Order' filter applied--> |
|||
<template id="report_order"> |
|||
<div class="page"> |
|||
<div class="oe_structure"/> |
|||
<span t-if="Filters.get('date_from')"> |
|||
<strong>From:</strong> |
|||
<t t-esc="Filters['date_from']"/> |
|||
</span> |
|||
<span t-if="Filters.get('date_to')"> |
|||
<strong>To:</strong> |
|||
<t t-esc="Filters['date_to']"/> |
|||
</span> |
|||
<div> |
|||
<div style="width:100%;"> |
|||
<div style="text-align:centre;" class="row"> |
|||
|
|||
<div class="col-2"> |
|||
<strong>Report Type:</strong> |
|||
<t t-esc="Filters.get('report_type')"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<br/> |
|||
<table class="table table-sm table-reports"> |
|||
<thead> |
|||
<tr> |
|||
<th class="text-left">Order</th> |
|||
<th colspan="6" class="text-center">Date Order</th> |
|||
<th colspan="6" class="text-right">Customer</th> |
|||
<th colspan="6" class="text-right">Purchase |
|||
Representative |
|||
</th> |
|||
<th colspan="6" class="text-center">Total Qty</th> |
|||
<th colspan="6" class="text-left">Amount Total</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody class="text-left"> |
|||
<t t-foreach="report_main_line_data" t-as="main"> |
|||
<tr style="font-weight: bold;"> |
|||
<td colspan="6"> |
|||
<span t-esc="main['name']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['date_order']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['partner']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['salesman']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['sum']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['amount_total']"/> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<br/> |
|||
</div> |
|||
</template> |
|||
<!--Report template when 'Report By Order Detail' filter applied--> |
|||
<template id="report_order_detail"> |
|||
<div class="page"> |
|||
<div class="oe_structure"/> |
|||
<span t-if="Filters.get('date_from')"> |
|||
<strong>From:</strong> |
|||
<t t-esc="Filters['date_from']"/> |
|||
</span> |
|||
<span t-if="Filters.get('date_to')"> |
|||
<strong>To:</strong> |
|||
<t t-esc="Filters['date_to']"/> |
|||
</span> |
|||
<div> |
|||
<div style="width:100%;"> |
|||
<div style="text-align:centre;" class="row"> |
|||
|
|||
<div class="col-2"> |
|||
<strong>Report Type:</strong> |
|||
<t t-esc="Filters.get('report_type')"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<br/> |
|||
<table class="table table-sm table-reports"> |
|||
<thead> |
|||
<tr class="text-right"> |
|||
<th>Order</th> |
|||
<th colspan="6">Date Order</th> |
|||
<th colspan="6">Customer</th> |
|||
<th colspan="6">Purchase Representative</th> |
|||
<th colspan="6">Product Code</th> |
|||
<th colspan="6">Product Name</th> |
|||
<th colspan="6">Price unit</th> |
|||
<th colspan="6">Qty</th> |
|||
<th colspan="6">Price Total</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="report_main_line_data" t-as="main"> |
|||
<tr style="font-weight: bold;"> |
|||
<td colspan="6"> |
|||
<span t-esc="main['name']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['date_order']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['partner']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['salesman']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['default_code']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['product']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['price_unit']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['sum']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['amount_total']"/> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<br/> |
|||
</div> |
|||
</template> |
|||
<!--Report template when 'Report By Product' filter applied--> |
|||
<template id="report_product"> |
|||
<div class="page"> |
|||
<div class="oe_structure"/> |
|||
<span t-if="Filters.get('date_from')"> |
|||
<strong>From:</strong> |
|||
<t t-esc="Filters['date_from']"/> |
|||
</span> |
|||
<span t-if="Filters.get('date_to')"> |
|||
<strong>To:</strong> |
|||
<t t-esc="Filters['date_to']"/> |
|||
</span> |
|||
<div> |
|||
<div style="width:100%;"> |
|||
<div style="text-align:centre;" class="row"> |
|||
<div class="col-2"> |
|||
<strong>Report Type:</strong> |
|||
<t t-esc="Filters.get('report_type')"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<br/> |
|||
<table class="table table-sm table-reports"> |
|||
<thead> |
|||
<tr> |
|||
<th class="text-left">Category</th> |
|||
<th colspan="6" class="text-center">Product Code |
|||
</th> |
|||
<th colspan="6" class="text-center">Product Name |
|||
</th> |
|||
<th colspan="6" class="text-center">Qty</th> |
|||
<th colspan="6">Amount Total</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="report_main_line_data" t-as="main"> |
|||
<tr style="font-weight: bold;"> |
|||
<td colspan="6"> |
|||
<span t-esc="main['name']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['default_code']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['product']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['qty']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['amount_total']"/> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<br/> |
|||
</div> |
|||
</template> |
|||
<!--Report template when 'Report By Category' filter applied--> |
|||
<template id="report_category"> |
|||
<div class="page"> |
|||
<div class="oe_structure"/> |
|||
<span t-if="Filters.get('date_from')"> |
|||
<strong>From:</strong> |
|||
<t t-esc="Filters['date_from']"/> |
|||
</span> |
|||
<span t-if="Filters.get('date_to')"> |
|||
<strong>To:</strong> |
|||
<t t-esc="Filters['date_to']"/> |
|||
</span> |
|||
<div> |
|||
<div style="width:100%;"> |
|||
<div style="text-align:centre;" class="row"> |
|||
<div class="col-2"> |
|||
<strong>Report Type:</strong> |
|||
<t t-esc="Filters.get('report_type')"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<br/> |
|||
<table class="table table-sm table-reports"> |
|||
<thead> |
|||
<tr> |
|||
<th colspan="6">Category</th> |
|||
<th colspan="6">Qty</th> |
|||
<th colspan="6">Amount Total</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="report_main_line_data" |
|||
t-as="purchase_category"> |
|||
<tr style="font-weight: bold;"> |
|||
<td colspan="6"> |
|||
<span t-esc="purchase_category['name']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="purchase_category['qty']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="purchase_category['amount_total']"/> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<br/> |
|||
</div> |
|||
</template> |
|||
<!--Report template when 'Report By Purchase Representative' |
|||
filter applied--> |
|||
<template id="report_purchase_representative"> |
|||
<div class="page"> |
|||
<div class="oe_structure"/> |
|||
<span t-if="Filters.get('date_from')"> |
|||
<strong>From:</strong> |
|||
<t t-esc="Filters['date_from']"/> |
|||
</span> |
|||
<span t-if="Filters.get('date_to')"> |
|||
<strong>To:</strong> |
|||
<t t-esc="Filters['date_to']"/> |
|||
</span> |
|||
<div> |
|||
<div style="width:100%;"> |
|||
<div style="text-align:centre;" class="row"> |
|||
|
|||
<div class="col-2"> |
|||
<strong>Report Type:</strong> |
|||
<t t-esc="Filters.get('report_type')"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<br/> |
|||
<table class="table table-sm table-reports"> |
|||
<thead> |
|||
<tr> |
|||
<th>Purchase Representative</th> |
|||
<th colspan="6">Total Order</th> |
|||
<th colspan="6">Total Qty</th> |
|||
<th colspan="6">Total Amount</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="report_main_line_data" t-as="main"> |
|||
<tr style="font-weight: bold;"> |
|||
<td> |
|||
<span t-esc="main['name']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['order']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['qty']"/> |
|||
</td> |
|||
<td colspan="6"> |
|||
<span t-esc="main['amount']"/> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<br/> |
|||
</div> |
|||
</template> |
|||
<!--Report template when 'Report By State' filter applied--> |
|||
<template id="report_state"> |
|||
<div class="page"> |
|||
<div class="oe_structure"/> |
|||
<span t-if="Filters.get('date_from')"> |
|||
<strong>From:</strong> |
|||
<t t-esc="Filters['date_from']"/> |
|||
</span> |
|||
<span t-if="Filters.get('date_to')"> |
|||
<strong>To:</strong> |
|||
<t t-esc="Filters['date_to']"/> |
|||
</span> |
|||
<div> |
|||
<div style="width:100%;"> |
|||
<div style="text-align:centre;" class="row"> |
|||
|
|||
<div class="col-2"> |
|||
<strong>Report Type:</strong> |
|||
<t t-esc="Filters.get('report_type')"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<br/> |
|||
<table class="table table-sm table-reports"> |
|||
<thead> |
|||
<tr class="text-left"> |
|||
<th>State</th> |
|||
<th colspan="6">Total Count</th> |
|||
<th colspan="6">Quantity</th> |
|||
<th colspan="6">Amount</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="report_main_line_data" t-as="main"> |
|||
<tr style="font-weight: bold;"> |
|||
<td colspan="6"> |
|||
<t t-if="main['state'] == 'draft'"> |
|||
<span>Quotation</span> |
|||
</t> |
|||
<t t-if="main['state'] == 'sent'"> |
|||
<span>Quotation Sent</span> |
|||
</t> |
|||
<t t-if="main['state'] == 'purchase'"> |
|||
<span>Purchase Order</span> |
|||
</t> |
|||
</td> |
|||
<td colspan="6" class="text-left"> |
|||
<span t-esc="main['order']"/> |
|||
</td> |
|||
<td colspan="6" class="text-left"> |
|||
<span t-esc="main['qty']"/> |
|||
</td> |
|||
<td colspan="6" class="text-left"> |
|||
<span t-esc="main['amount']"/> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<br/> |
|||
</div> |
|||
</template> |
|||
</odoo> |
@ -0,0 +1,15 @@ |
|||
<?xml version="1.0" encoding="utf-8" ?> |
|||
<odoo> |
|||
<!--Print amount in words in bill--> |
|||
<template id="invoice_report_view_amount_in_words" |
|||
inherit_id="account.report_invoice_document"> |
|||
<xpath expr="//div[@id='payment_term']" position="before"> |
|||
<div style="font-size:15px; padding-top:15px; padding-bottom:15px;"> |
|||
<span> |
|||
<strong>Amount in Words (Total) :</strong> |
|||
</span> |
|||
<span t-field="o.number_to_words"/> |
|||
</div> |
|||
</xpath> |
|||
</template> |
|||
</odoo> |
@ -0,0 +1,206 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<!--Purchase requisition report template--> |
|||
<template id="report_purchase_requisition"> |
|||
<t t-call="web.external_layout"> |
|||
<t t-call="web.html_container"> |
|||
<div class="page"> |
|||
<t t-foreach="records" t-as="rec"> |
|||
<h1>Purchase Requisition : |
|||
<t t-if="rec['name']"> |
|||
<t t-esc="rec['name']"/> |
|||
</t> |
|||
</h1> |
|||
<table class="table table-striped"> |
|||
<tr> |
|||
<th>Employee</th> |
|||
<td> |
|||
<t t-if="rec['employee_id']"> |
|||
<t t-esc="rec['employee_id'][1]"/> |
|||
</t> |
|||
</td> |
|||
<th>Requisition Date</th> |
|||
<td> |
|||
<t t-if="rec['employee_id']"> |
|||
<t t-esc="rec['requisition_date']"/> |
|||
</t> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<th>Department</th> |
|||
<td> |
|||
<t t-if="rec['dept_id']"> |
|||
<t t-esc="rec['dept_id'][1]"/> |
|||
</t> |
|||
</td> |
|||
<th>Received Date</th> |
|||
<td> |
|||
<t t-if="rec['receive_date']"> |
|||
<t t-esc="rec['receive_date']"/> |
|||
</t> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<th>Requisition Responsible</th> |
|||
<td> |
|||
<t t-if="rec['user_id']"> |
|||
<t t-esc="rec['user_id'][1]"/> |
|||
</t> |
|||
</td> |
|||
<th>Requisition Deadline</th> |
|||
<td> |
|||
<t t-if="rec['requisition_deadline']"> |
|||
<t t-esc="rec['requisition_deadline']"/> |
|||
</t> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<th>Company</th> |
|||
<td> |
|||
<t t-if="rec['company_id']"> |
|||
<t t-esc="rec['company_id'][1]"/> |
|||
</t> |
|||
</td> |
|||
</tr> |
|||
</table> |
|||
<h1>Other Information</h1> |
|||
<table class="table table-sm"> |
|||
<tr> |
|||
<th>Confirmed By</th> |
|||
<td> |
|||
<t t-if="rec['confirm_id']"> |
|||
<t t-esc="rec['confirm_id'][1]"/> |
|||
</t> |
|||
</td> |
|||
<th>Confirmed Date</th> |
|||
<td> |
|||
<t t-if="rec['confirmed_date']"> |
|||
<t t-esc="rec['confirmed_date']"/> |
|||
</t> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<th>Department Manager</th> |
|||
<td> |
|||
<t t-if="rec['manager_id']"> |
|||
<t t-esc="rec['manager_id'][1]"/> |
|||
</t> |
|||
</td> |
|||
<th>Department Approval Date</th> |
|||
<td> |
|||
<t t-if="rec['department_approval_date']"> |
|||
<t t-esc="rec['department_approval_date']"/> |
|||
</t> |
|||
</td> |
|||
</tr> |
|||
|
|||
<tr> |
|||
<th>Approved By:</th> |
|||
<td> |
|||
<t t-if="rec['requisition_head_id']"> |
|||
<t t-esc="rec['requisition_head_id'][1]"/> |
|||
</t> |
|||
</td> |
|||
<th>Approved Date:</th> |
|||
<td> |
|||
<t t-if="rec['approval_date']"> |
|||
<t t-esc="rec['approval_date']"/> |
|||
</t> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<th>Rejected By</th> |
|||
<td> |
|||
<t t-if="rec['rejected_user_id']"> |
|||
<t t-esc="rec['rejected_user_id'][1]"/> |
|||
</t> |
|||
</td> |
|||
<th>Rejected Date</th> |
|||
<td> |
|||
<t t-if="rec['reject_date']"> |
|||
<t t-esc="rec['reject_date']"/> |
|||
</t> |
|||
</td> |
|||
</tr> |
|||
</table> |
|||
<h1>Picking Details</h1> |
|||
<table class="table table-sm"> |
|||
<tr> |
|||
<th>Source Location</th> |
|||
<td> |
|||
<t t-if="rec['source_location_id']"> |
|||
<t t-esc="rec['source_location_id'][1]"/> |
|||
</t> |
|||
</td> |
|||
<th>Destination Location</th> |
|||
<td> |
|||
<t t-if="rec['source_location_id']"> |
|||
<t t-esc="rec['destination_location_id'][1]"/> |
|||
</t> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<t t-if="rec['delivery_type_id']"> |
|||
<th>Delivery To</th> |
|||
<td> |
|||
<t t-esc="rec['delivery_type_id'][1]"/> |
|||
</td> |
|||
</t> |
|||
<t t-if="rec['internal_picking_id']"> |
|||
<th>Internal Transfer</th> |
|||
<td> |
|||
<t t-esc="rec['internal_picking_id'][1]"/> |
|||
</td> |
|||
</t> |
|||
</tr> |
|||
</table> |
|||
</t> |
|||
<h1>Requisition Lines</h1> |
|||
<table class='table'> |
|||
<thead> |
|||
<th>Requisition Action</th> |
|||
<th>Product</th> |
|||
<th>Description</th> |
|||
<th>Quantity</th> |
|||
<th>Unit Of Measure</th> |
|||
</thead> |
|||
<t t-foreach="order_ids" t-as="order"> |
|||
<tbody> |
|||
<td> |
|||
<t t-if="order['requisition_type']=='internal_transfer'"> |
|||
Internal Transfer |
|||
</t> |
|||
<t t-if="order['requisition_type']=='purchase_order'"> |
|||
Purchase Order |
|||
</t> |
|||
</td> |
|||
<td> |
|||
<t t-if="order['product_id']"> |
|||
<t t-esc="order['product_id'][1]"/> |
|||
</t> |
|||
</td> |
|||
<td> |
|||
<t t-if="order['description']"> |
|||
<t t-esc="order['description']"/> |
|||
</t> |
|||
</td> |
|||
<td> |
|||
<t t-if="order['quantity']"> |
|||
<t t-esc="order['quantity']"/> |
|||
</t> |
|||
</td> |
|||
<td><t t-if="order['uom']"> |
|||
<t t-esc="order['uom']"/> |
|||
</t> |
|||
</td> |
|||
</tbody> |
|||
</t> |
|||
</table> |
|||
<h3>Employee Signature :</h3> |
|||
<h3>Manager Signature :</h3> |
|||
<h3>Approve Signature :</h3> |
|||
</div> |
|||
</t> |
|||
</t> |
|||
</template> |
|||
</odoo> |
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<odoo> |
|||
<record id="employee_requisition" model="ir.module.category"> |
|||
<field name="name">Employee Purchase Requisition</field> |
|||
<field name="description">User access level for Material Request |
|||
module |
|||
</field> |
|||
<field name="sequence">20</field> |
|||
</record> |
|||
<!--User groups--> |
|||
<!--User requisition--> |
|||
<record id="employee_requisition_user" model="res.groups"> |
|||
<field name="name">Requisition Users</field> |
|||
<field name="category_id" ref="employee_requisition"/> |
|||
</record> |
|||
<!--Requisition rights for head--> |
|||
<record id="employee_requisition_head" model="res.groups"> |
|||
<field name="name">Department Head</field> |
|||
<field name="category_id" ref="employee_requisition"/> |
|||
<field name="implied_ids" |
|||
eval="[(4, ref('all_in_one_purchase_kit.employee_requisition_user'))]"/> |
|||
</record> |
|||
<!--Requisition rights for manager--> |
|||
<record id="employee_requisition_manager" model="res.groups"> |
|||
<field name="name">Requisition Manager</field> |
|||
<field name="category_id" ref="employee_requisition"/> |
|||
<field name="implied_ids" |
|||
eval="[(4, ref('all_in_one_purchase_kit.employee_requisition_head'))]"/> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,34 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<!--Record rules--> |
|||
<!--Rule for User--> |
|||
<record id="requisition_user_rule" model="ir.rule"> |
|||
<field name="name">Requisition User Rule</field> |
|||
<field name="model_id" ref="model_employee_purchase_requisition"/> |
|||
<field name="domain_force">[('confirm_id', '=', user.id)]</field> |
|||
<field name="groups" |
|||
eval="[(4, ref('all_in_one_purchase_kit.employee_requisition_user'))]"/> |
|||
</record> |
|||
<!--Rule for head--> |
|||
<record id="department_head_rule" model="ir.rule"> |
|||
<field name="name">Department Head Rule</field> |
|||
<field ref="model_employee_purchase_requisition" name="model_id"/> |
|||
<field name="domain_force">[('user_id','=',user.id)]</field> |
|||
<field name="groups" |
|||
eval="[(4, ref('all_in_one_purchase_kit.employee_requisition_head'))]"/> |
|||
</record> |
|||
<!--Rule for manager--> |
|||
<record id="requisition_manager_rule" model="ir.rule"> |
|||
<field name="name">Requisition Manager Rule</field> |
|||
<field ref="model_employee_purchase_requisition" name="model_id"/> |
|||
<field name="domain_force">[(1,'=',1)]</field> |
|||
<field name="groups" |
|||
eval="[(4, ref('all_in_one_purchase_kit.employee_requisition_manager'))]"/> |
|||
</record> |
|||
<!--Company rule--> |
|||
<record id="requisition_company_rule" model="ir.rule"> |
|||
<field name="name">Purchase Requisition Company Rule</field> |
|||
<field ref="model_employee_purchase_requisition" name="model_id"/> |
|||
<field name="domain_force">[('company_id', '=', company_id)]</field> |
|||
</record> |
|||
</odoo> |
|
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 589 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 565 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 967 B |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 117 KiB |
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 160 KiB |
After Width: | Height: | Size: 90 KiB |
After Width: | Height: | Size: 190 KiB |
After Width: | Height: | Size: 335 KiB |
After Width: | Height: | Size: 205 KiB |
After Width: | Height: | Size: 141 KiB |
After Width: | Height: | Size: 153 KiB |
After Width: | Height: | Size: 177 KiB |
After Width: | Height: | Size: 189 KiB |
After Width: | Height: | Size: 210 KiB |
After Width: | Height: | Size: 205 KiB |
After Width: | Height: | Size: 194 KiB |
After Width: | Height: | Size: 188 KiB |
After Width: | Height: | Size: 145 KiB |