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 000000000..31d219ce1 Binary files /dev/null and b/stock_batch_picking_voucher/report/batch_picking_preprinted.odt differ 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 + + +