From a10f7628f8722dd33d9418c980d0d26a8b32fae7 Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Thu, 21 Jul 2016 13:29:49 +0200 Subject: [PATCH 01/61] [9.0] port module delivery_carrier_label_dispatch - change name to batch to match with change of `picking_dispatch` name to `stock_batch_picking` - mode description in README.rst - move files in models and views directories - change headers to small license header - use api 8.0 - fix and improve tests --- delivery_carrier_label_batch/README.rst | 59 ++++++++ delivery_carrier_label_batch/__init__.py | 2 + delivery_carrier_label_batch/__openerp__.py | 26 ++++ delivery_carrier_label_batch/i18n/de.po | 142 ++++++++++++++++++ .../i18n/delivery_carrier_label_dispatch.pot | 139 +++++++++++++++++ delivery_carrier_label_batch/i18n/fr.po | 140 +++++++++++++++++ .../models/__init__.py | 1 + .../models/stock_batch_picking.py | 111 ++++++++++++++ delivery_carrier_label_batch/pdf_utils.py | 33 ++++ .../tests/__init__.py | 21 +++ delivery_carrier_label_batch/tests/dummy.pdf | Bin 0 -> 16947 bytes .../tests/test_generate_labels.py | 103 +++++++++++++ .../views/stock_batch_picking.xml | 25 +++ .../wizard/__init__.py | 2 + .../wizard/apply_carrier.py | 39 +++++ .../wizard/apply_carrier_view.xml | 40 +++++ .../wizard/generate_labels.py | 112 ++++++++++++++ .../wizard/generate_labels_view.xml | 43 ++++++ 18 files changed, 1038 insertions(+) create mode 100644 delivery_carrier_label_batch/README.rst create mode 100644 delivery_carrier_label_batch/__init__.py create mode 100644 delivery_carrier_label_batch/__openerp__.py create mode 100644 delivery_carrier_label_batch/i18n/de.po create mode 100644 delivery_carrier_label_batch/i18n/delivery_carrier_label_dispatch.pot create mode 100644 delivery_carrier_label_batch/i18n/fr.po create mode 100644 delivery_carrier_label_batch/models/__init__.py create mode 100644 delivery_carrier_label_batch/models/stock_batch_picking.py create mode 100644 delivery_carrier_label_batch/pdf_utils.py create mode 100644 delivery_carrier_label_batch/tests/__init__.py create mode 100644 delivery_carrier_label_batch/tests/dummy.pdf create mode 100644 delivery_carrier_label_batch/tests/test_generate_labels.py create mode 100644 delivery_carrier_label_batch/views/stock_batch_picking.xml create mode 100644 delivery_carrier_label_batch/wizard/__init__.py create mode 100644 delivery_carrier_label_batch/wizard/apply_carrier.py create mode 100644 delivery_carrier_label_batch/wizard/apply_carrier_view.xml create mode 100644 delivery_carrier_label_batch/wizard/generate_labels.py create mode 100644 delivery_carrier_label_batch/wizard/generate_labels_view.xml diff --git a/delivery_carrier_label_batch/README.rst b/delivery_carrier_label_batch/README.rst new file mode 100644 index 0000000000..ad1ff2d509 --- /dev/null +++ b/delivery_carrier_label_batch/README.rst @@ -0,0 +1,59 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +=============================================== +[Link module] Carrier labels - Picking dispatch +=============================================== + +This module adds a wizard on picking batch to generate the labels +of the packs. The labels are merged in one PDF file. + +If you want multiple labels for one picking, all the moves should have been +put in a pack before the labels can be printed. + +If you don't define your pack it will be considered a picking is a single pack. + + +Usage +===== + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/99/9.0 + +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 +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Yannick Vaucher + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org.[Link module] Carrier labels - Picking Batch diff --git a/delivery_carrier_label_batch/__init__.py b/delivery_carrier_label_batch/__init__.py new file mode 100644 index 0000000000..9b4296142f --- /dev/null +++ b/delivery_carrier_label_batch/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizard diff --git a/delivery_carrier_label_batch/__openerp__.py b/delivery_carrier_label_batch/__openerp__.py new file mode 100644 index 0000000000..6ae079dfeb --- /dev/null +++ b/delivery_carrier_label_batch/__openerp__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Copyright 2013-2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +{ + 'name': 'Carrier labels - Stock Batch Picking (link)', + 'version': '9.0.1.0.0', + 'author': "Camptocamp,Odoo Community Association (OCA)", + 'maintainer': 'Camptocamp', + 'category': 'Carrier', + 'complexity': 'normal', + 'depends': ['base_delivery_carrier_label', 'stock_batch_picking'], + 'website': 'http://www.camptocamp.com/', + 'data': [ + 'views/stock_batch_picking.xml', + 'wizard/generate_labels_view.xml', + 'wizard/apply_carrier_view.xml', + ], + 'tests': [], + 'installable': True, + 'auto_install': True, + 'license': 'AGPL-3', + 'application': False, + 'external_dependencies': { + 'python': ['PyPDF2'], + } +} diff --git a/delivery_carrier_label_batch/i18n/de.po b/delivery_carrier_label_batch/i18n/de.po new file mode 100644 index 0000000000..464dfd1129 --- /dev/null +++ b/delivery_carrier_label_batch/i18n/de.po @@ -0,0 +1,142 @@ +# German translation for carriers-deliveries +# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 +# This file is distributed under the same license as the carriers-deliveries package. +# FIRST AUTHOR , 2014. +# +msgid "" +msgstr "" +"Project-Id-Version: carriers-deliveries\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2014-01-09 13:29+0000\n" +"PO-Revision-Date: 2015-01-05 14:14+0100\n" +"Last-Translator: Rudolf Schnapka \n" +"Language-Team: German \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2014-06-27 07:09+0000\n" +"X-Generator: Poedit 1.5.4\n" +"Language: de\n" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch:0 +msgid "Carrier Info" +msgstr "Information zum Spediteur" + +#. module: delivery_carrier_label_dispatch +#: model:ir.actions.act_window,name:delivery_carrier_label_dispatch.action_delivery_carrier_label_generate +msgid "Generate Carrier Labels" +msgstr "Paketaufkleber erzeugen" + +#. module: delivery_carrier_label_dispatch +#: model:ir.model,name:delivery_carrier_label_dispatch.model_stock_picking +msgid "Picking List" +msgstr "Kommissionierschein" + +#. module: delivery_carrier_label_dispatch +#: code:addons/delivery_carrier_label_dispatch/picking_dispatch.py:100 +#, python-format +msgid "" +"You can not remove a mandatory option.\n" +"Options are reset to default." +msgstr "" +"Sie können eine Pflichtoption nicht entfernen.\n" +"Auswahl wird auf Vorgaben zurückgesetzt." + +#. module: delivery_carrier_label_dispatch +#: model:ir.model,name:delivery_carrier_label_dispatch.model_picking_dispatch +msgid "Dispatch Picking Order" +msgstr "Kommissionierung auslösen" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch:0 +msgid "Delivery" +msgstr "Lieferung" + +#. module: delivery_carrier_label_dispatch +#: code:addons/delivery_carrier_label_dispatch/wizard/generate_labels.py:58 +#, python-format +msgid "No picking dispatch selected" +msgstr "Keine Kommissionierung ausgewählt" + +#. module: delivery_carrier_label_dispatch +#: field:delivery.carrier.label.generate,label_pdf_file:0 +#: model:ir.model.fields,field_description:delivery_carrier_label_dispatch.field_delivery_carrier_label_generate_label_pdf_file +msgid "Labels file" +msgstr "Etikettdatei" + +#. module: delivery_carrier_label_dispatch +#: field:delivery.carrier.label.generate,dispatch_ids:0 +#: model:ir.model.fields,field_description:delivery_carrier_label_dispatch.field_delivery_carrier_label_generate_dispatch_ids +msgid "Picking Dispatch" +msgstr "Kommissionierauftrag" + +#. module: delivery_carrier_label_dispatch +#: model:ir.model,name:delivery_carrier_label_dispatch.model_delivery_carrier_label_generate +msgid "delivery.carrier.label.generate" +msgstr "delivery.carrier.label.generate" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch:0 +msgid "Set Options" +msgstr "Optionen setzen" + +#. module: delivery_carrier_label_dispatch +#: model:ir.model.fields,field_description:delivery_carrier_label_dispatch.field_picking_dispatch_carrier_id +#: field:picking.dispatch,carrier_id:0 +msgid "Carrier" +msgstr "Spediteur" + +#. module: delivery_carrier_label_dispatch +#: code:addons/delivery_carrier_label_dispatch/wizard/generate_labels.py:58 +#, python-format +msgid "Error" +msgstr "Fehler" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch:0 +msgid "Warning, setting options will erase the existing ones in delivery orders" +msgstr "" +"Warnung: Diese Einstellungen zur Kommissionierung überschreiben vorherige" + +#. module: delivery_carrier_label_dispatch +#: model:ir.model.fields,field_description:delivery_carrier_label_dispatch.field_picking_dispatch_option_ids +#: view:picking.dispatch:0 field:picking.dispatch,option_ids:0 +msgid "Options" +msgstr "Einstellungen" + +#. module: delivery_carrier_label_dispatch +#: view:delivery.carrier.label.generate:0 +msgid "Generate Labels" +msgstr "Etiketten erzeugen" + +#. module: delivery_carrier_label_dispatch +#: view:delivery.carrier.label.generate:0 +msgid "Labels" +msgstr "Etiketten" + +#. module: delivery_carrier_label_dispatch +#: view:delivery.carrier.label.generate:0 +msgid "Generate Carriers Labels" +msgstr "Etiketten des Spediteurs erzeugen" + +#. module: delivery_carrier_label_dispatch +#: view:delivery.carrier.label.generate:0 +msgid "Close" +msgstr "Schließen" + +#. module: delivery_carrier_label_dispatch +#: code:addons/delivery_carrier_label_dispatch/picking_dispatch.py:99 +#, python-format +msgid "User Error !" +msgstr "Anwenderfehler!" + +#. module: delivery_carrier_label_dispatch +#: model:ir.model,name:delivery_carrier_label_dispatch.model_stock_picking_out +msgid "Delivery Orders" +msgstr "Lieferaufträge" + +#. module: delivery_carrier_label_dispatch +#: view:delivery.carrier.label.generate:0 +msgid "or" +msgstr "oder" diff --git a/delivery_carrier_label_batch/i18n/delivery_carrier_label_dispatch.pot b/delivery_carrier_label_batch/i18n/delivery_carrier_label_dispatch.pot new file mode 100644 index 0000000000..703a98896f --- /dev/null +++ b/delivery_carrier_label_batch/i18n/delivery_carrier_label_dispatch.pot @@ -0,0 +1,139 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * delivery_carrier_label_dispatch +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-09-09 12:11+0000\n" +"PO-Revision-Date: 2014-04-10 14:03+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch.apply.carrier:0 +msgid "Apply" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: model:ir.actions.act_window,name:delivery_carrier_label_dispatch.action_picking_dispatch_apply_carrier +#: view:picking.dispatch.apply.carrier:0 +msgid "Apply a carrier and its options" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch.apply.carrier:0 +msgid "Cancel" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: model:ir.model.fields,field_description:delivery_carrier_label_dispatch.field_picking_dispatch_apply_carrier_carrier_id +#: model:ir.model.fields,field_description:delivery_carrier_label_dispatch.field_picking_dispatch_carrier_id +#: field:picking.dispatch,carrier_id:0 +#: field:picking.dispatch.apply.carrier,carrier_id:0 +msgid "Carrier" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch:0 +msgid "Carrier Info" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: view:delivery.carrier.label.generate:0 +msgid "Close" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch:0 +msgid "Delivery" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: code:_description:0 +#: model:ir.model,name:delivery_carrier_label_dispatch.model_picking_dispatch +#, python-format +msgid "Dispatch Picking Order" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: model:ir.actions.act_window,name:delivery_carrier_label_dispatch.action_delivery_carrier_label_generate +msgid "Generate Carrier Labels" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: view:delivery.carrier.label.generate:0 +msgid "Generate Carriers Labels" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: view:delivery.carrier.label.generate:0 +msgid "Generate Labels" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: view:delivery.carrier.label.generate:0 +msgid "Labels" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: field:delivery.carrier.label.generate,label_pdf_file:0 +#: model:ir.model.fields,field_description:delivery_carrier_label_dispatch.field_delivery_carrier_label_generate_label_pdf_file +msgid "Labels file" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: model:ir.model.fields,field_description:delivery_carrier_label_dispatch.field_picking_dispatch_option_ids +#: view:picking.dispatch:0 field:picking.dispatch,option_ids:0 +msgid "Options" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: field:delivery.carrier.label.generate,dispatch_ids:0 +#: model:ir.model.fields,field_description:delivery_carrier_label_dispatch.field_delivery_carrier_label_generate_dispatch_ids +msgid "Picking Dispatch" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: code:_description:0 +#: model:ir.model,name:delivery_carrier_label_dispatch.model_picking_dispatch_apply_carrier +#, python-format +msgid "Picking Dispatch Apply Carrier" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: code:_description:0 +#: model:ir.model,name:delivery_carrier_label_dispatch.model_stock_picking +#, python-format +msgid "Picking List" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch:0 +msgid "Set Options" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch:0 +#: view:picking.dispatch.apply.carrier:0 +msgid "Warning, setting options will erase the existing ones in delivery orders" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: code:_description:0 +#: model:ir.model,name:delivery_carrier_label_dispatch.model_delivery_carrier_label_generate +#, python-format +msgid "delivery.carrier.label.generate" +msgstr "" + +#. module: delivery_carrier_label_dispatch +#: view:delivery.carrier.label.generate:0 +#: view:picking.dispatch.apply.carrier:0 +msgid "or" +msgstr "" diff --git a/delivery_carrier_label_batch/i18n/fr.po b/delivery_carrier_label_batch/i18n/fr.po new file mode 100644 index 0000000000..ee0a8e92e8 --- /dev/null +++ b/delivery_carrier_label_batch/i18n/fr.po @@ -0,0 +1,140 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * delivery_carrier_label_dispatch +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-09-09 12:11+0000\n" +"PO-Revision-Date: 2014-09-09 12:11+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch.apply.carrier:0 +msgid "Apply" +msgstr "Appliquer" + +#. module: delivery_carrier_label_dispatch +#: model:ir.actions.act_window,name:delivery_carrier_label_dispatch.action_picking_dispatch_apply_carrier +#: view:picking.dispatch.apply.carrier:0 +msgid "Apply a carrier and its options" +msgstr "Appliquer un transporteur et ses options" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch.apply.carrier:0 +msgid "Cancel" +msgstr "Annuler" + +#. module: delivery_carrier_label_dispatch +#: model:ir.model.fields,field_description:delivery_carrier_label_dispatch.field_picking_dispatch_apply_carrier_carrier_id +#: model:ir.model.fields,field_description:delivery_carrier_label_dispatch.field_picking_dispatch_carrier_id +#: field:picking.dispatch,carrier_id:0 +#: field:picking.dispatch.apply.carrier,carrier_id:0 +msgid "Carrier" +msgstr "Transporteur" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch:0 +msgid "Carrier Info" +msgstr "Info transporteur" + +#. module: delivery_carrier_label_dispatch +#: view:delivery.carrier.label.generate:0 +msgid "Close" +msgstr "Fermer" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch:0 +msgid "Delivery" +msgstr "Livraison" + +#. module: delivery_carrier_label_dispatch +#: code:_description:0 +#: model:ir.model,name:delivery_carrier_label_dispatch.model_picking_dispatch +#, python-format +msgid "Dispatch Picking Order" +msgstr "Bon de préparation" + +#. module: delivery_carrier_label_dispatch +#: model:ir.actions.act_window,name:delivery_carrier_label_dispatch.action_delivery_carrier_label_generate +msgid "Generate Carrier Labels" +msgstr "Générer les étiquette d’expédition" + +#. module: delivery_carrier_label_dispatch +#: view:delivery.carrier.label.generate:0 +msgid "Generate Carriers Labels" +msgstr "Générer les étiquettes d'expédition" + +#. module: delivery_carrier_label_dispatch +#: view:delivery.carrier.label.generate:0 +msgid "Generate Labels" +msgstr "Générer les étiquettes" + +#. module: delivery_carrier_label_dispatch +#: view:delivery.carrier.label.generate:0 +msgid "Labels" +msgstr "Etiquettes" + +#. module: delivery_carrier_label_dispatch +#: field:delivery.carrier.label.generate,label_pdf_file:0 +#: model:ir.model.fields,field_description:delivery_carrier_label_dispatch.field_delivery_carrier_label_generate_label_pdf_file +msgid "Labels file" +msgstr "Fichier d'étiquettes" + +#. module: delivery_carrier_label_dispatch +#: model:ir.model.fields,field_description:delivery_carrier_label_dispatch.field_picking_dispatch_option_ids +#: view:picking.dispatch:0 +#: field:picking.dispatch,option_ids:0 +msgid "Options" +msgstr "Options" + +#. module: delivery_carrier_label_dispatch +#: field:delivery.carrier.label.generate,dispatch_ids:0 +#: model:ir.model.fields,field_description:delivery_carrier_label_dispatch.field_delivery_carrier_label_generate_dispatch_ids +msgid "Picking Dispatch" +msgstr "Bon de préparation" + +#. module: delivery_carrier_label_dispatch +#: code:_description:0 +#: model:ir.model,name:delivery_carrier_label_dispatch.model_picking_dispatch_apply_carrier +#, python-format +msgid "Picking Dispatch Apply Carrier" +msgstr "Applique un transporteur sur des bons de préparation" + +#. module: delivery_carrier_label_dispatch +#: code:_description:0 +#: model:ir.model,name:delivery_carrier_label_dispatch.model_stock_picking +#, python-format +msgid "Picking List" +msgstr "Bon de livraison" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch:0 +msgid "Set Options" +msgstr "Appliquer les options" + +#. module: delivery_carrier_label_dispatch +#: view:picking.dispatch:0 +#: view:picking.dispatch.apply.carrier:0 +msgid "Warning, setting options will erase the existing ones in delivery orders" +msgstr "Attention, définir les options supprimera les options existantes dans les livraisons du bon de préparation" + +#. module: delivery_carrier_label_dispatch +#: code:_description:0 +#: model:ir.model,name:delivery_carrier_label_dispatch.model_delivery_carrier_label_generate +#, python-format +msgid "delivery.carrier.label.generate" +msgstr "delivery.carrier.label.generate" + +#. module: delivery_carrier_label_dispatch +#: view:delivery.carrier.label.generate:0 +#: view:picking.dispatch.apply.carrier:0 +msgid "or" +msgstr "ou" + diff --git a/delivery_carrier_label_batch/models/__init__.py b/delivery_carrier_label_batch/models/__init__.py new file mode 100644 index 0000000000..d477460d17 --- /dev/null +++ b/delivery_carrier_label_batch/models/__init__.py @@ -0,0 +1 @@ +from . import stock_batch_picking diff --git a/delivery_carrier_label_batch/models/stock_batch_picking.py b/delivery_carrier_label_batch/models/stock_batch_picking.py new file mode 100644 index 0000000000..04317149c3 --- /dev/null +++ b/delivery_carrier_label_batch/models/stock_batch_picking.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +# Copyright 2013-2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +from openerp import _, api, fields, models + + +class StockBatchPicking(models.Model): + + """ Add carrier and carrier options on batch + + to be able to massively set those options on related picking. + + """ + _inherit = "stock.batch.picking" + + carrier_id = fields.Many2one( + 'delivery.carrier', 'Carrier', + states={'done': [('readonly', True)]}) + option_ids = fields.Many2many( + 'delivery.carrier.option', + string='Options') + + @api.multi + def action_set_options(self): + """ Apply options to picking of the batch + + This will replace all carrier options in picking + + """ + for rec in self: + options_datas = { + 'carrier_id': rec.carrier_id.id, + 'option_ids': [(6, 0, rec.option_ids)], + } + rec.picking_ids.write(options_datas) + + @api.multi + def _get_options_to_add(self, carrier=None): + carrier = carrier or self.carrier_id + options = carrier.available_option_ids + return options.filtered(lambda rec: rec.mandatory or rec.by_default) + + @api.onchange('carrier_id') + def carrier_id_change(self): + """ Inherit this method in your module """ + if self.carrier_id: + # This can look useless as the field carrier_code and + # carrier_type are related field. But it's needed to fill + # this field for using this fields in the view. Indeed the + # module that depend on delivery base can hide some field + # depending of the type or the code + + available_options = self.carrier_id.available_option_ids + self.option_ids = self._get_options_to_add() + self.carrier_type = self.carrier_id.type + self.carrier_code = self.carrier_id.code + return {'domain': { + 'option_ids': [('id', 'in', available_options.ids)], + }} + return {} + + @api.onchange('option_ids') + def option_ids_change(self): + res = {} + if not self.carrier_id: + return res + for available_option in self.carrier_id.available_option_ids: + if (available_option.mandatory and + available_option not in self.option_ids): + res['warning'] = { + 'title': _('User Error !'), + 'message': _("You can not remove a mandatory option." + "\nOptions are reset to default.") + } + # trigger carrier_id change to reset default values + self.carrier_id = self.carrier_id + return res + + @api.multi + def _values_with_carrier_options(self, values): + values = values.copy() + carrier_id = values.get('carrier_id') + option_ids = values.get('option_ids') + if carrier_id and not option_ids: + carrier = self.env['delivery.carrier'].browse(carrier_id) + options = self._get_options_to_add(carrier) + if options: + values.update(option_ids=[(6, 0, options.ids)]) + return values + + @api.multi + def write(self, values): + """ Set the default options when the delivery method is changed. + + So we are sure that the options are always in line with the + current delivery method. + + """ + values = self._values_with_carrier_options(values) + return super(StockBatchPicking, self).write(values) + + @api.model + def create(self, values): + """ Set the default options when the delivery method is set on creation + + So we are sure that the options are always in line with the + current delivery method. + + """ + values = self._values_with_carrier_options(values) + return super(StockBatchPicking, self).create(values) diff --git a/delivery_carrier_label_batch/pdf_utils.py b/delivery_carrier_label_batch/pdf_utils.py new file mode 100644 index 0000000000..c9ebfcbcf1 --- /dev/null +++ b/delivery_carrier_label_batch/pdf_utils.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Copyright 2013-2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +from StringIO import StringIO +from PyPDF2 import PdfFileReader, PdfFileWriter + + +def assemble_pdf(pdf_list): + """ + Assemble a list of pdf + """ + # Even though we are using PyPDF2 we can't use PdfFileMerger + # as this issue still exists in mostly used wkhtmltohpdf reports version + # http://code.google.com/p/wkhtmltopdf/issues/detail?id=635 + # merger = PdfFileMerger() + # merger.append(fileobj=StringIO(invoice_pdf)) + # merger.append(fileobj=StringIO(bvr_pdf)) + + # with tempfile.TemporaryFile() as merged_pdf: + # merger.write(merged_pdf) + # return merged_pdf.read(), 'pdf' + + output = PdfFileWriter() + for pdf in pdf_list: + if not pdf: + continue + reader = PdfFileReader(StringIO(pdf)) + + for page in range(reader.getNumPages()): + output.addPage(reader.getPage(page)) + s = StringIO() + output.write(s) + return s.getvalue() diff --git a/delivery_carrier_label_batch/tests/__init__.py b/delivery_carrier_label_batch/tests/__init__.py new file mode 100644 index 0000000000..1bcac6e22f --- /dev/null +++ b/delivery_carrier_label_batch/tests/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Yannick Vaucher +# Copyright 2013 Camptocamp SA +# +# 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 . +# +############################################################################## +from . import test_generate_labels diff --git a/delivery_carrier_label_batch/tests/dummy.pdf b/delivery_carrier_label_batch/tests/dummy.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2be598a24e76f3b555ab2f675f829199a094b68a GIT binary patch literal 16947 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 k^Hx@{yXppW-{zLvL3M4Rx3>(4jhT%djfO@ + + + + stock.batch.picking.form + stock.batch.picking + + + + + + + + + + +