From 895aa6ac4993484f18db3ec8424f415dc6f8c052 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Carreras Date: Fri, 13 Sep 2024 13:18:57 -0300 Subject: [PATCH] [IMP]stock_batch_picking_voucher:new module --- stock_batch_picking_voucher/README.rst | 65 ++++++++++ stock_batch_picking_voucher/__init__.py | 7 + stock_batch_picking_voucher/__manifest__.py | 50 +++++++ .../controllers/__init__.py | 1 + .../controllers/main.py | 42 ++++++ .../models/__init__.py | 3 + .../models/stock_batch_picking.py | 44 +++++++ .../models/stock_picking.py | 22 ++++ .../models/stock_picking_voucher.py | 42 ++++++ .../report/batch_picking_preprinted.odt | Bin 0 -> 22047 bytes .../report/batch_picking_preprinted_data.xml | 16 +++ .../security/ir.model.access.csv | 5 + .../views/stock_batch_picking_views.xml | 42 ++++++ .../views/stock_picking_views.xml | 22 ++++ .../wizards/__init__.py | 1 + .../stock_print_batch_stock_voucher.py | 122 ++++++++++++++++++ .../stock_print_batch_stock_voucher_views.xml | 51 ++++++++ 17 files changed, 535 insertions(+) create mode 100644 stock_batch_picking_voucher/README.rst create mode 100644 stock_batch_picking_voucher/__init__.py create mode 100644 stock_batch_picking_voucher/__manifest__.py create mode 100644 stock_batch_picking_voucher/controllers/__init__.py create mode 100644 stock_batch_picking_voucher/controllers/main.py create mode 100644 stock_batch_picking_voucher/models/__init__.py create mode 100644 stock_batch_picking_voucher/models/stock_batch_picking.py create mode 100644 stock_batch_picking_voucher/models/stock_picking.py create mode 100644 stock_batch_picking_voucher/models/stock_picking_voucher.py create mode 100644 stock_batch_picking_voucher/report/batch_picking_preprinted.odt create mode 100644 stock_batch_picking_voucher/report/batch_picking_preprinted_data.xml create mode 100644 stock_batch_picking_voucher/security/ir.model.access.csv create mode 100644 stock_batch_picking_voucher/views/stock_batch_picking_views.xml create mode 100644 stock_batch_picking_voucher/views/stock_picking_views.xml create mode 100644 stock_batch_picking_voucher/wizards/__init__.py create mode 100644 stock_batch_picking_voucher/wizards/stock_print_batch_stock_voucher.py create mode 100644 stock_batch_picking_voucher/wizards/stock_print_batch_stock_voucher_views.xml diff --git a/stock_batch_picking_voucher/README.rst b/stock_batch_picking_voucher/README.rst new file mode 100644 index 000000000..44be64e94 --- /dev/null +++ b/stock_batch_picking_voucher/README.rst @@ -0,0 +1,65 @@ +.. |company| replace:: ADHOC SA + +.. |company_logo| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-logo.png + :alt: ADHOC SA + :target: https://www.adhoc.com.ar + +.. |icon| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-icon.png + +.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: https://www.gnu.org/licenses/agpl + :alt: License: AGPL-3 + +==================================== +Pre-printed report in batch pickings +==================================== + +This module add the following features: +#. Add aeroo report to print Pre-printed from batch pickings + +Installation +============ + +To install this module, you need to: + +#. Only need to install the module + +Configuration +============= + +To configure this module, you need to: + +#. Nothing to configure + + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: http://runbot.adhoc.com.ar/ + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* |company| |icon| + +Contributors +------------ + +Maintainer +---------- + +|company_logo| + +This module is maintained by the |company|. + +To contribute to this module, please visit https://www.adhoc.com.ar. diff --git a/stock_batch_picking_voucher/__init__.py b/stock_batch_picking_voucher/__init__.py new file mode 100644 index 000000000..0c0cc42f4 --- /dev/null +++ b/stock_batch_picking_voucher/__init__.py @@ -0,0 +1,7 @@ +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from . import models +from . import wizards +from . import controllers diff --git a/stock_batch_picking_voucher/__manifest__.py b/stock_batch_picking_voucher/__manifest__.py new file mode 100644 index 000000000..d197bcea6 --- /dev/null +++ b/stock_batch_picking_voucher/__manifest__.py @@ -0,0 +1,50 @@ +############################################################################## +# +# Copyright (C) 2015 ADHOC SA (http://www.adhoc.com.ar) +# All Rights Reserved. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# 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 for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +{ + 'name': 'Preprinted report in batch pickings', + 'version': "17.0.1.0.0", + 'category': 'Warehouse Management', + 'sequence': 14, + 'summary': '', + 'author': 'ADHOC SA', + 'website': 'www.adhoc.com.ar', + 'license': 'AGPL-3', + 'images': [ + ], + 'depends': [ + 'stock_batch_picking_ux', + 'report_aeroo', + 'l10n_latam_base', + 'delivery_ux' + ], + 'data': [ + 'security/ir.model.access.csv', + 'wizards/stock_print_batch_stock_voucher_views.xml', + 'report/batch_picking_preprinted_data.xml', + 'views/stock_batch_picking_views.xml', + 'views/stock_picking_views.xml', + + ], + 'demo': [ + ], + 'installable': True, + 'auto_install': True, + 'application': False, +} diff --git a/stock_batch_picking_voucher/controllers/__init__.py b/stock_batch_picking_voucher/controllers/__init__.py new file mode 100644 index 000000000..12a7e529b --- /dev/null +++ b/stock_batch_picking_voucher/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/stock_batch_picking_voucher/controllers/main.py b/stock_batch_picking_voucher/controllers/main.py new file mode 100644 index 000000000..57b041bc0 --- /dev/null +++ b/stock_batch_picking_voucher/controllers/main.py @@ -0,0 +1,42 @@ +import io +import json +import urllib.parse + +from odoo.http import route, request +from odoo.addons.web.controllers import report +from PyPDF2 import PdfFileReader + + +class ReportController(report.ReportController): + + @route() + def report_download(self, data, context=None): + """This function is used by 'qwebactionmanager.js' in order to trigger + the download of a py3o/controller report. + :param data: a javascript array JSON.stringified containg report + internal url ([0]) and type [1] + :returns: Response with a filetoken cookie and an attachment header + """ + response = super().report_download(data, context) + #NTH detect if the binary is a PDF, no matter ifn it was generated by a QWeb or Aeroo + requestcontent = json.loads(data) + url, type = requestcontent[0], requestcontent[1] + if type != 'aeroo': + return response + + json_string = json.loads(data)[0] + context_part = json_string.split('context=')[1] + decoded_context = urllib.parse.unquote(context_part) + context_dict = json.loads(decoded_context) + batch_id = context_dict.get('active_id') + batch = context_dict.get('batch') + book_id = request.env['stock.picking.batch'].browse(batch_id).book_id + if batch: + if batch_id: + pdf_response = response.response[0] + reader = PdfFileReader(io.BytesIO(pdf_response)) + number_pages = reader.getNumPages() + if not request.env['stock.picking.batch'].browse(batch_id).voucher_ids: + request.env['stock.picking.batch'].browse(batch_id).assign_numbers(number_pages, book_id) + + return response \ No newline at end of file diff --git a/stock_batch_picking_voucher/models/__init__.py b/stock_batch_picking_voucher/models/__init__.py new file mode 100644 index 000000000..e083e0f80 --- /dev/null +++ b/stock_batch_picking_voucher/models/__init__.py @@ -0,0 +1,3 @@ +from . import stock_picking_voucher +from . import stock_batch_picking +# from . import stock_picking \ No newline at end of file diff --git a/stock_batch_picking_voucher/models/stock_batch_picking.py b/stock_batch_picking_voucher/models/stock_batch_picking.py new file mode 100644 index 000000000..82d04c5a2 --- /dev/null +++ b/stock_batch_picking_voucher/models/stock_batch_picking.py @@ -0,0 +1,44 @@ +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from odoo import fields, api, models, _ + +class StockPickingBatch(models.Model): + _inherit = 'stock.picking.batch' + + voucher_ids = fields.One2many( + 'stock.picking.voucher', + 'batch_id', + 'Remitos', + copy=False, + ) + + book_id = fields.Many2one( + 'stock.book', + 'Talonario', + copy=False, + ondelete='restrict', + check_company=True + ) + + def assign_numbers(self, estimated_number_of_pages, book): + self.ensure_one() + list_of_vouchers = [] + for page in range(estimated_number_of_pages): + list_of_vouchers.append({ + 'name': book.sequence_id.next_by_id(), + 'book_id': book.id, + 'batch_id' : self.id, + }) + self.env['stock.picking.voucher'].create(list_of_vouchers) + self.message_post(body=_( + 'Números de remitos asignados: %s') % (self.vouchers)) + self.write({'book_id': book.id}) + return { + 'type': 'ir.actions.act_window', + 'res_model': 'stock.picking.batch', + 'view_mode': 'form', + 'res_id': self.id, + 'target': 'current', + } \ No newline at end of file diff --git a/stock_batch_picking_voucher/models/stock_picking.py b/stock_batch_picking_voucher/models/stock_picking.py new file mode 100644 index 000000000..bfa780b75 --- /dev/null +++ b/stock_batch_picking_voucher/models/stock_picking.py @@ -0,0 +1,22 @@ +# flake8: noqa +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from odoo import models, fields, api, _ +from odoo.exceptions import ValidationError, UserError + + +class StockPicking(models.Model): + + _inherit = 'stock.picking' + + batch_vouchers = printed = fields.Boolean(compute = '_batch_vouchers') + + def _batch_vouchers(self): + for rec in self: + if rec.batch_id and rec.batch_id.voucher_ids: + return True + else: + return False + diff --git a/stock_batch_picking_voucher/models/stock_picking_voucher.py b/stock_batch_picking_voucher/models/stock_picking_voucher.py new file mode 100644 index 000000000..6922d0cac --- /dev/null +++ b/stock_batch_picking_voucher/models/stock_picking_voucher.py @@ -0,0 +1,42 @@ +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from odoo import fields, models, api, _ +from odoo.exceptions import ValidationError + + +class StockPickingVoucher(models.Model): + _inherit = 'stock.picking.voucher' + + batch_id = fields.Many2one( + 'stock.picking.batch', + 'Batch', + ondelete='cascade', + index=True, + ) + + picking_id = fields.Many2one( + 'stock.picking', + 'Picking', + ondelete='cascade', + required=False, + index=True, + ) + + @api.constrains('picking_id', 'batch_id') + def _check_picking_id_required(self): + for record in self: + if not record.batch_id and not record.picking_id: + raise ValidationError("Al crear un voucher debe estar ligado a una trasnferencia o lote de transferencias") + + @api.model + def create(self, vals): + if 'batch_id' in vals and vals['batch_id']: + vals['picking_id'] = False + return super(StockPickingVoucher, self).create(vals) + + def write(self, vals): + if 'batch_id' in vals and vals['batch_id']: + vals['picking_id'] = False + return super(StockPickingVoucher, self).write(vals) \ No newline at end of file diff --git a/stock_batch_picking_voucher/report/batch_picking_preprinted.odt b/stock_batch_picking_voucher/report/batch_picking_preprinted.odt new file mode 100644 index 0000000000000000000000000000000000000000..31d219ce12f8dcb2317061717da6767704b75aad GIT binary patch literal 22047 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 literal 0 HcmV?d00001 diff --git a/stock_batch_picking_voucher/report/batch_picking_preprinted_data.xml b/stock_batch_picking_voucher/report/batch_picking_preprinted_data.xml new file mode 100644 index 000000000..94fd2a0d5 --- /dev/null +++ b/stock_batch_picking_voucher/report/batch_picking_preprinted_data.xml @@ -0,0 +1,16 @@ + + + + Preprinted Voucher + stock.picking.batch + batch_picking_preprinted + aeroo + oo-odt + stock_batch_picking_voucher/report/batch_picking_preprinted.odt + + file + + report + + + diff --git a/stock_batch_picking_voucher/security/ir.model.access.csv b/stock_batch_picking_voucher/security/ir.model.access.csv new file mode 100644 index 000000000..aa0d6a2eb --- /dev/null +++ b/stock_batch_picking_voucher/security/ir.model.access.csv @@ -0,0 +1,5 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +stock_batch_picking_voucher.access_stock_print_batch_stock_voucher,access_stock_print_batch_stock_voucher,stock_batch_picking_voucher.model_stock_print_batch_stock_voucher,base.group_user,1,1,1,0 +stock_batch_picking_voucher.access_stock_voucher,access_stock_voucher,model_stock_picking_voucher,base.group_user,1,1,1,0 + + diff --git a/stock_batch_picking_voucher/views/stock_batch_picking_views.xml b/stock_batch_picking_voucher/views/stock_batch_picking_views.xml new file mode 100644 index 000000000..6646f14ab --- /dev/null +++ b/stock_batch_picking_voucher/views/stock_batch_picking_views.xml @@ -0,0 +1,42 @@ + + + + stock.picking.batch.form + stock.picking.batch + + + + + + + + + + + + stock.picking.tree.inherited + stock.picking + + + + ('picking_type_code','=','incoming') or ('state','!=','done') or batch_id + + + + + + stock.picking.tree.inherited2 + stock.picking + + + + not book_id or picking_type_code == 'incoming' or state != 'done' or batch_id + + + book_id or picking_type_code == 'incoming' or state != 'done' or batch_id + + + + diff --git a/stock_batch_picking_voucher/views/stock_picking_views.xml b/stock_batch_picking_voucher/views/stock_picking_views.xml new file mode 100644 index 000000000..e33bc6ef2 --- /dev/null +++ b/stock_batch_picking_voucher/views/stock_picking_views.xml @@ -0,0 +1,22 @@ + + + stock.picking.form.exception + stock.picking + 99 + + + + + + + + batch_id + + + batch_id + + + + \ No newline at end of file diff --git a/stock_batch_picking_voucher/wizards/__init__.py b/stock_batch_picking_voucher/wizards/__init__.py new file mode 100644 index 000000000..4329a8de7 --- /dev/null +++ b/stock_batch_picking_voucher/wizards/__init__.py @@ -0,0 +1 @@ +from . import stock_print_batch_stock_voucher diff --git a/stock_batch_picking_voucher/wizards/stock_print_batch_stock_voucher.py b/stock_batch_picking_voucher/wizards/stock_print_batch_stock_voucher.py new file mode 100644 index 000000000..96d5fa3e2 --- /dev/null +++ b/stock_batch_picking_voucher/wizards/stock_print_batch_stock_voucher.py @@ -0,0 +1,122 @@ +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from odoo import fields, api, models, _ + + +class StockPrintStockVoucher(models.TransientModel): + _name = 'stock.print_batch_stock_voucher' + _description = "Print batch Stock Voucher" + + + @api.model + def _get_pickings(self): + # if we came, for eg, from a sale order, active_id would be the + # self._context.get('active_id')) + picking_ids = self.env[self._context.get('active_model')].browse(int(self._context.get('active_id'))).picking_ids + return picking_ids + + @api.model + def _get_book(self): + picking = self._get_pickings() + return picking.book_id or self.env['stock.book'].search([('company_id', '=', picking.company_id.id)], limit=1) + + picking_ids = fields.Many2many( + 'stock.picking', + default= lambda self: self._get_pickings(), + required=True, + ) + + printed = fields.Boolean( + ) + with_vouchers = fields.Boolean( + compute='_compute_with_vouchers', + ) + book_id = fields.Many2one( + 'stock.book', + 'Book', + default=lambda self: self._get_book(), + ) + next_voucher_number = fields.Integer( + 'Next Voucher Number', + related='book_id.sequence_id.number_next_actual', + ) + estimated_number_of_pages = fields.Integer( + 'Number of Pages', + ) + lines_per_voucher = fields.Integer( + 'Lines Per Voucher', + related='book_id.lines_per_voucher', + ) + + @api.depends('picking_ids', 'picking_ids.voucher_ids') + def _compute_with_vouchers(self): + for rec in self: + rec.with_vouchers = bool(self.picking_ids[:1].batch_id.voucher_ids) + + @api.onchange('picking_ids') + def set_book_domain(self): + picking = self._get_pickings() + if not picking: + return {} + else: + return {'domain': {'book_id': [('company_id', '=', picking.company_id.id)]}} + + @api.onchange('book_id', 'picking_ids') + def get_estimated_number_of_pages(self): + lines_per_voucher = self.lines_per_voucher + if lines_per_voucher == 0: + self.estimated_number_of_pages = 1 + return + + operations = len(self.picking_ids.move_line_ids) + estimated_number_of_pages = int( + -(-float(operations) // float(lines_per_voucher))) + self.estimated_number_of_pages = estimated_number_of_pages + + def do_print_voucher(self): + self.printed = True + if self.book_id: + self.picking_ids[:1].batch_id.book_id = self.book_id.id + return self.do_print_batch_vouchers(self.picking_ids[:1].batch_id) + + # def assign_numbers(self): + # # import pdb;pdb.set_trace() + # import pdb;pdb.set_trace() + # self.assign_numbers_in_batch( + # self.estimated_number_of_pages, self.book_id) + + + # def do_print_and_assign(self): + # self.assign_numbers() + # return { + # 'actions': [ + # {'type': 'ir.actions.act_window_close'}, + # self.do_print_voucher(), + # ], + # 'type': 'ir.actions.act_multi', + # } + + def do_print_and_assign(self): + # We override the method to avoid assignation + if self.book_id.lines_per_voucher != 0: + return { + 'actions': [ + {'type': 'ir.actions.act_window_close'}, + self.with_context(batch=True).do_print_voucher(), + ], + 'type': 'ir.actions.act_multi' + } + self.picking_ids[:1].batch_id.assign_numbers(1,self.book_id) + return self.do_print_batch_vouchers(self.picking_ids[:1].batch_id) + + def do_print_batch_vouchers(self, batch): + '''This function prints the voucher''' + return self.env.ref('stock_batch_picking_voucher.batch_picking_preprinted').report_action(batch) + + def do_clean(self): + batch = self.picking_ids[:1].batch_id + batch.voucher_ids.unlink() + batch.book_id = False + batch.message_post(body=_('The assigned voucher were deleted')) diff --git a/stock_batch_picking_voucher/wizards/stock_print_batch_stock_voucher_views.xml b/stock_batch_picking_voucher/wizards/stock_print_batch_stock_voucher_views.xml new file mode 100644 index 000000000..91d886f7a --- /dev/null +++ b/stock_batch_picking_voucher/wizards/stock_print_batch_stock_voucher_views.xml @@ -0,0 +1,51 @@ + + + + print_stock_picking.form + stock.print_batch_stock_voucher + +
+
+

THE VOUCHER IT'S PRINTED AND ASSIGNED.

+
+ + + + + + + + + + + +
+ +
+
+
+
+ + + Print Stock batch Remits + stock.print_batch_stock_voucher + form + + new + + +