[tryton-debian-vcs] tryton-modules-sale branch debian updated. debian/3.0.0-3-4-gd9feba7

Mathias Behrle tryton-debian-vcs at alioth.debian.org
Tue Apr 22 13:09:53 UTC 2014


The following commit has been merged in the debian branch:
https://alioth.debian.org/plugins/scmgit/cgi-bin/gitweb.cgi/?p=tryton/tryton-modules-sale.git;a=commitdiff;h=debian/3.0.0-3-4-gd9feba7

commit d9feba70ff942a5c6eec3b8828aede33634e5c3a
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Tue Apr 22 15:00:11 2014 +0200

    Bumping minimal required Python version to 2.7.

diff --git a/debian/control b/debian/control
index b3059eb..6005f64 100644
--- a/debian/control
+++ b/debian/control
@@ -9,7 +9,7 @@ Standards-Version: 3.9.5
 Homepage: http://www.tryton.org/
 Vcs-Browser: http://anonscm.debian.org/gitweb/?p=tryton/tryton-modules-sale.git
 Vcs-Git: git://anonscm.debian.org/tryton/tryton-modules-sale.git
-X-Python-Version: >= 2.6
+X-Python-Version: >= 2.7
 
 Package: tryton-modules-sale
 Architecture: all
commit 475d076bfc3910e20d07526b07143afd98fe0772
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Tue Apr 22 14:46:39 2014 +0200

    Updating copyright.

diff --git a/debian/copyright b/debian/copyright
index fdd8122..75eceed 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,9 +1,9 @@
 Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 
 Files: *
-Copyright: 2008-2013 Cédric Krier
+Copyright: 2008-2014 Cédric Krier
            2008-2013 Bertrand Chenal
-           2008-2013 B2CK SPRL
+           2008-2014 B2CK SPRL
            2004-2008 Tiny SPRL
 License: GPL-3+
 
commit d7f0612e302fd24a31bf041d3548264db6c6d81f
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Tue Apr 22 14:23:16 2014 +0200

    Merging upstream version 3.2.0.

diff --git a/CHANGELOG b/CHANGELOG
index abd2b7b..d8eab5b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,8 @@
+Version 3.2.0 - 2014-04-21
+* Bug fixes (see mercurial logs for details)
+* Add link between stock moves and invoice lines
+* Add warning for customer moves without origin
+
 Version 3.0.0 - 2013-10-21
 * Bug fixes (see mercurial logs for details)
 * Replace reference copying by relate
diff --git a/COPYRIGHT b/COPYRIGHT
index 5d422e0..77715d8 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -1,6 +1,6 @@
-Copyright (C) 2008-2013 Cédric Krier.
+Copyright (C) 2008-2014 Cédric Krier.
 Copyright (C) 2008-2013 Bertrand Chenal.
-Copyright (C) 2008-2013 B2CK SPRL.
+Copyright (C) 2008-2014 B2CK SPRL.
 Copyright (C) 2004-2008 Tiny SPRL.
 
 This program is free software: you can redistribute it and/or modify
diff --git a/INSTALL b/INSTALL
index aba12d0..eba6ba3 100644
--- a/INSTALL
+++ b/INSTALL
@@ -4,7 +4,7 @@ Installing trytond_sale
 Prerequisites
 -------------
 
- * Python 2.6 or later (http://www.python.org/)
+ * Python 2.7 or later (http://www.python.org/)
  * trytond (http://www.tryton.org/)
  * python-sql (http://code.google.com/p/python-sql/)
  * trytond_company (http://www.tryton.org/)
diff --git a/MANIFEST.in b/MANIFEST.in
index fc0b03e..05c3f79 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,6 +1,5 @@
 include INSTALL
 include README
-include TODO
 include COPYRIGHT
 include CHANGELOG
 include LICENSE
diff --git a/PKG-INFO b/PKG-INFO
index 95adefa..57e44db 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,12 +1,12 @@
 Metadata-Version: 1.1
 Name: trytond_sale
-Version: 3.0.0
+Version: 3.2.0
 Summary: Tryton module for sale
 Home-page: http://www.tryton.org/
 Author: Tryton
-Author-email: UNKNOWN
+Author-email: issue_tracker at tryton.org
 License: GPL-3
-Download-URL: http://downloads.tryton.org/3.0/
+Download-URL: http://downloads.tryton.org/3.2/
 Description: trytond_sale
         ============
         
@@ -43,6 +43,7 @@ Description: trytond_sale
         
           http://www.tryton.org/
         
+Keywords: tryton sale
 Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Plugins
@@ -62,7 +63,6 @@ Classifier: Natural Language :: Russian
 Classifier: Natural Language :: Slovenian
 Classifier: Natural Language :: Spanish
 Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Topic :: Office/Business
 Classifier: Topic :: Office/Business :: Financial :: Accounting
diff --git a/__init__.py b/__init__.py
index 03ce289..0b4e671 100644
--- a/__init__.py
+++ b/__init__.py
@@ -13,7 +13,6 @@ def register():
     Pool.register(
         Move,
         Sale,
-        SaleInvoice,
         SaleIgnoredInvoice,
         SaleRecreatedInvoice,
         SaleLine,
diff --git a/invoice.py b/invoice.py
index caa6056..d2d4c74 100644
--- a/invoice.py
+++ b/invoice.py
@@ -17,29 +17,28 @@ def process_sale(func):
     def wrapper(cls, invoices):
         pool = Pool()
         Sale = pool.get('sale.sale')
+        with Transaction().set_user(0, set_context=True):
+            sales = [s for i in cls.browse(invoices) for s in i.sales]
         func(cls, invoices)
         with Transaction().set_user(0, set_context=True):
-            Sale.process([s for i in cls.browse(invoices)
-                    for s in i.sales])
+            Sale.process(sales)
     return wrapper
 
 
 class Invoice:
     __name__ = 'account.invoice'
-    sales = fields.Many2Many('sale.sale-account.invoice',
-            'invoice', 'sale', 'Sales', readonly=True)
     sale_exception_state = fields.Function(fields.Selection([
         ('', ''),
         ('ignored', 'Ignored'),
         ('recreated', 'Recreated'),
         ], 'Exception State'), 'get_sale_exception_state')
+    sales = fields.Function(fields.One2Many('sale.sale', None, 'Sales'),
+        'get_sales', searcher='search_sales')
 
     @classmethod
     def __setup__(cls):
         super(Invoice, cls).__setup__()
         cls._error_messages.update({
-                'delete_sale_invoice': ('You can not delete invoices '
-                    'that come from a sale.'),
                 'reset_invoice_sale': ('You cannot reset to draft '
                     'an invoice generated by a sale.'),
                 })
@@ -64,6 +63,20 @@ class Invoice:
                 states[invoice.id] = 'ignored'
         return states
 
+    def get_sales(self, name):
+        pool = Pool()
+        SaleLine = pool.get('sale.line')
+        sales = set()
+        for line in self.lines:
+            if isinstance(line.origin, SaleLine):
+                sales.add(line.origin.sale.id)
+        return list(sales)
+
+    @classmethod
+    def search_sales(cls, name, clause):
+        return [('lines.origin.sale.id',) + tuple(clause[1:])
+            + ('sale.line',)]
+
     @classmethod
     def copy(cls, invoices, default=None):
         if default is None:
@@ -73,17 +86,8 @@ class Invoice:
         return super(Invoice, cls).copy(invoices, default=default)
 
     @classmethod
+    @process_sale
     def delete(cls, invoices):
-        pool = Pool()
-        Sale_Invoice = pool.get('sale.sale-account.invoice')
-        sale_invoice = Sale_Invoice.__table__()
-        cursor = Transaction().cursor
-        if invoices:
-            cursor.execute(*sale_invoice.select(sale_invoice.id,
-                    where=sale_invoice.invoice.in_(
-                        [i.id for i in invoices])))
-            if cursor.fetchone():
-                cls.raise_user_error('delete_sale_invoice')
         super(Invoice, cls).delete(invoices)
 
     @classmethod
diff --git a/locale/bg_BG.po b/locale/bg_BG.po
index cd5fe24..c81a465 100644
--- a/locale/bg_BG.po
+++ b/locale/bg_BG.po
@@ -3,10 +3,6 @@ msgid ""
 msgstr "Content-Type: text/plain; charset=utf-8\n"
 
 msgctxt "error:account.invoice:"
-msgid "You can not delete invoices that come from a sale."
-msgstr ""
-
-msgctxt "error:account.invoice:"
 msgid "You cannot reset to draft an invoice generated by a sale."
 msgstr "Не може да прехвърляте в проект фактура генерирана при продажба."
 
@@ -497,38 +493,6 @@ msgctxt "field:sale.sale,write_uid:"
 msgid "Write User"
 msgstr "Променено от"
 
-msgctxt "field:sale.sale-account.invoice,create_date:"
-msgid "Create Date"
-msgstr "Създадено на"
-
-msgctxt "field:sale.sale-account.invoice,create_uid:"
-msgid "Create User"
-msgstr "Създадено от"
-
-msgctxt "field:sale.sale-account.invoice,id:"
-msgid "ID"
-msgstr "ID"
-
-msgctxt "field:sale.sale-account.invoice,invoice:"
-msgid "Invoice"
-msgstr "Фактура"
-
-msgctxt "field:sale.sale-account.invoice,rec_name:"
-msgid "Name"
-msgstr "Име"
-
-msgctxt "field:sale.sale-account.invoice,sale:"
-msgid "Sale"
-msgstr "Продажба"
-
-msgctxt "field:sale.sale-account.invoice,write_date:"
-msgid "Write Date"
-msgstr "Променено на"
-
-msgctxt "field:sale.sale-account.invoice,write_uid:"
-msgid "Write User"
-msgstr "Променено от"
-
 msgctxt "field:sale.sale-ignored-account.invoice,create_date:"
 msgid "Create Date"
 msgstr "Създадено на"
@@ -766,10 +730,6 @@ msgctxt "model:sale.sale,name:"
 msgid "Sale"
 msgstr "Продажба"
 
-msgctxt "model:sale.sale-account.invoice,name:"
-msgid "Sale - Invoice"
-msgstr "Продажба - фактура"
-
 msgctxt "model:sale.sale-ignored-account.invoice,name:"
 msgid "Sale - Ignored Invoice"
 msgstr "Продажба - Игнорирани фактури"
diff --git a/locale/ca_ES.po b/locale/ca_ES.po
index 0d4c76b..ca79ec9 100644
--- a/locale/ca_ES.po
+++ b/locale/ca_ES.po
@@ -3,10 +3,6 @@ msgid ""
 msgstr "Content-Type: text/plain; charset=utf-8\n"
 
 msgctxt "error:account.invoice:"
-msgid "You can not delete invoices that come from a sale."
-msgstr "No podeu esborrar factures generades des de vendes."
-
-msgctxt "error:account.invoice:"
 msgid "You cannot reset to draft an invoice generated by a sale."
 msgstr "No podeu restaurar a esborrany una factura generada per una venda."
 
@@ -18,7 +14,7 @@ msgstr ""
 msgctxt "error:sale.line:"
 msgid "Sale \"%(sale)s\" is missing the customer location in line \"%(line)s\"."
 msgstr ""
-"No es troba la localizació del client a la línia \"%(line)s\" de la venda "
+"No es troba la localització del client a la línia \"%(line)s\" de la venda "
 "\"%(sale)\"."
 
 msgctxt "error:sale.line:"
@@ -28,7 +24,8 @@ msgstr "Falta la propietat \"Compte a cobrar\" per defecte de les vendes."
 msgctxt "error:sale.sale:"
 msgid "Invalid combination of shipment and invoicing methods on sale \"%s\"."
 msgstr ""
-"Combinació de mètodes d'enviament i facturació a la venda \"%s\" incorrecta."
+"La combinació de mètodes d'enviament i facturació a la venda \"%s\" no és "
+"correcta."
 
 msgctxt "error:sale.sale:"
 msgid ""
@@ -460,7 +457,7 @@ msgstr "Estat enviament"
 
 msgctxt "field:sale.sale,shipments:"
 msgid "Shipments"
-msgstr "Enviaments"
+msgstr "Albarans"
 
 msgctxt "field:sale.sale,state:"
 msgid "State"
@@ -502,38 +499,6 @@ msgctxt "field:sale.sale,write_uid:"
 msgid "Write User"
 msgstr "Usuari modificació"
 
-msgctxt "field:sale.sale-account.invoice,create_date:"
-msgid "Create Date"
-msgstr "Data creació"
-
-msgctxt "field:sale.sale-account.invoice,create_uid:"
-msgid "Create User"
-msgstr "Usuari creació"
-
-msgctxt "field:sale.sale-account.invoice,id:"
-msgid "ID"
-msgstr "ID"
-
-msgctxt "field:sale.sale-account.invoice,invoice:"
-msgid "Invoice"
-msgstr "Factura"
-
-msgctxt "field:sale.sale-account.invoice,rec_name:"
-msgid "Name"
-msgstr "Nom"
-
-msgctxt "field:sale.sale-account.invoice,sale:"
-msgid "Sale"
-msgstr "Venda"
-
-msgctxt "field:sale.sale-account.invoice,write_date:"
-msgid "Write Date"
-msgstr "Data modificació"
-
-msgctxt "field:sale.sale-account.invoice,write_uid:"
-msgid "Write User"
-msgstr "Usuari modificació"
-
 msgctxt "field:sale.sale-ignored-account.invoice,create_date:"
 msgid "Create Date"
 msgstr "Data creació"
@@ -608,7 +573,7 @@ msgstr "Estat excepció"
 
 msgctxt "help:product.template,delivery_time:"
 msgid "In number of days"
-msgstr "En nombre de dies"
+msgstr "En nombre de dies."
 
 msgctxt "help:sale.handle.invoice.exception.ask,recreate_invoices:"
 msgid ""
@@ -763,10 +728,6 @@ msgctxt "model:sale.sale,name:"
 msgid "Sale"
 msgstr "Venda"
 
-msgctxt "model:sale.sale-account.invoice,name:"
-msgid "Sale - Invoice"
-msgstr "Venda - Factura"
-
 msgctxt "model:sale.sale-ignored-account.invoice,name:"
 msgid "Sale - Ignored Invoice"
 msgstr "Venda - Factura ignorada"
@@ -797,7 +758,7 @@ msgstr "Comanda de Venda Esborrany"
 
 msgctxt "odt:sale.sale:"
 msgid "E-Mail:"
-msgstr "Email:"
+msgstr "Correu electrònic:"
 
 msgctxt "odt:sale.sale:"
 msgid "Phone:"
@@ -809,11 +770,11 @@ msgstr "Quantitat"
 
 msgctxt "odt:sale.sale:"
 msgid "Quotation N°:"
-msgstr "Pressupost Nº:"
+msgstr "Pressupost Núm.:"
 
 msgctxt "odt:sale.sale:"
 msgid "Sale Order N°:"
-msgstr "Venta Nº:"
+msgstr "Venta Núm.:"
 
 msgctxt "odt:sale.sale:"
 msgid "Taxes"
@@ -853,7 +814,7 @@ msgstr "Ignorat"
 
 msgctxt "selection:account.invoice,sale_exception_state:"
 msgid "Recreated"
-msgstr "Recreat"
+msgstr "Recreada"
 
 msgctxt "selection:sale.configuration,sale_invoice_method:"
 msgid "Manual"
@@ -917,7 +878,7 @@ msgstr "Cap"
 
 msgctxt "selection:sale.sale,invoice_state:"
 msgid "Paid"
-msgstr "Pagat"
+msgstr "Pagada"
 
 msgctxt "selection:sale.sale,invoice_state:"
 msgid "Waiting"
@@ -945,7 +906,7 @@ msgstr "Cap"
 
 msgctxt "selection:sale.sale,shipment_state:"
 msgid "Sent"
-msgstr "Enviat"
+msgstr "Enviada"
 
 msgctxt "selection:sale.sale,shipment_state:"
 msgid "Waiting"
@@ -953,15 +914,15 @@ msgstr "En espera"
 
 msgctxt "selection:sale.sale,state:"
 msgid "Canceled"
-msgstr "Cancel·lat"
+msgstr "Cancel·lada"
 
 msgctxt "selection:sale.sale,state:"
 msgid "Confirmed"
-msgstr "Confirmat"
+msgstr "Confirmada"
 
 msgctxt "selection:sale.sale,state:"
 msgid "Done"
-msgstr "Finalitzat"
+msgstr "Finalitzada"
 
 msgctxt "selection:sale.sale,state:"
 msgid "Draft"
@@ -1037,7 +998,7 @@ msgstr "Línies de venda"
 
 msgctxt "view:sale.return_sale.start:"
 msgid "Are you sure to return those/this sale(s)?"
-msgstr "Esteu segur de retornar aquestes comandes?"
+msgstr "Esteu segur de retornar aquestes comandes de venda?"
 
 msgctxt "view:sale.return_sale.start:"
 msgid "Return Sale"
@@ -1089,7 +1050,7 @@ msgstr "Vendes"
 
 msgctxt "view:sale.sale:"
 msgid "Shipments"
-msgstr "Enviaments"
+msgstr "Albarans"
 
 msgctxt "view:stock.move:"
 msgid "Moves"
diff --git a/locale/cs_CZ.po b/locale/cs_CZ.po
index f7b505b..489994c 100644
--- a/locale/cs_CZ.po
+++ b/locale/cs_CZ.po
@@ -3,10 +3,6 @@ msgid ""
 msgstr "Content-Type: text/plain; charset=utf-8\n"
 
 msgctxt "error:account.invoice:"
-msgid "You can not delete invoices that come from a sale."
-msgstr ""
-
-msgctxt "error:account.invoice:"
 msgid "You cannot reset to draft an invoice generated by a sale."
 msgstr ""
 
@@ -496,38 +492,6 @@ msgctxt "field:sale.sale,write_uid:"
 msgid "Write User"
 msgstr ""
 
-msgctxt "field:sale.sale-account.invoice,create_date:"
-msgid "Create Date"
-msgstr ""
-
-msgctxt "field:sale.sale-account.invoice,create_uid:"
-msgid "Create User"
-msgstr ""
-
-msgctxt "field:sale.sale-account.invoice,id:"
-msgid "ID"
-msgstr ""
-
-msgctxt "field:sale.sale-account.invoice,invoice:"
-msgid "Invoice"
-msgstr ""
-
-msgctxt "field:sale.sale-account.invoice,rec_name:"
-msgid "Name"
-msgstr ""
-
-msgctxt "field:sale.sale-account.invoice,sale:"
-msgid "Sale"
-msgstr ""
-
-msgctxt "field:sale.sale-account.invoice,write_date:"
-msgid "Write Date"
-msgstr ""
-
-msgctxt "field:sale.sale-account.invoice,write_uid:"
-msgid "Write User"
-msgstr ""
-
 msgctxt "field:sale.sale-ignored-account.invoice,create_date:"
 msgid "Create Date"
 msgstr ""
@@ -756,10 +720,6 @@ msgctxt "model:sale.sale,name:"
 msgid "Sale"
 msgstr ""
 
-msgctxt "model:sale.sale-account.invoice,name:"
-msgid "Sale - Invoice"
-msgstr ""
-
 msgctxt "model:sale.sale-ignored-account.invoice,name:"
 msgid "Sale - Ignored Invoice"
 msgstr ""
diff --git a/locale/de_DE.po b/locale/de_DE.po
index 25e6266..b8e7746 100644
--- a/locale/de_DE.po
+++ b/locale/de_DE.po
@@ -3,10 +3,6 @@ msgid ""
 msgstr "Content-Type: text/plain; charset=utf-8\n"
 
 msgctxt "error:account.invoice:"
-msgid "You can not delete invoices that come from a sale."
-msgstr "Aus einem Verkauf stammende Rechnungen können nicht gelöscht werden."
-
-msgctxt "error:account.invoice:"
 msgid "You cannot reset to draft an invoice generated by a sale."
 msgstr ""
 "Eine durch einen Verkauf generierte Rechnung kann nicht auf Entwurf "
@@ -21,7 +17,7 @@ msgstr ""
 msgctxt "error:sale.line:"
 msgid "Sale \"%(sale)s\" is missing the customer location in line \"%(line)s\"."
 msgstr ""
-"Für Verkauf \"%(sale)s\" Position \"%(line)s\" ist kein Lagerort Kunde "
+"Für Position \"%(line)s\" von Verkauf \"%(sale)s\" ist kein Lagerort Kunde "
 "eingetragen."
 
 msgctxt "error:sale.line:"
@@ -67,7 +63,7 @@ msgstr ""
 msgctxt "error:stock.shipment.out:"
 msgid "You cannot reset to draft a move generated by a sale."
 msgstr ""
-"Eine durch einen Verkauf generierte Bewegung kann nicht auf Entwurf "
+"Eine durch einen Verkauf generierte Lagerbewegung kann nicht auf Entwurf "
 "zurückgesetzt werden."
 
 msgctxt "field:account.invoice,sale_exception_state:"
@@ -136,7 +132,7 @@ msgstr "ID"
 
 msgctxt "field:sale.handle.invoice.exception.ask,recreate_invoices:"
 msgid "Recreate Invoices"
-msgstr "Rechnungen nachbilden"
+msgstr "Rechnungen neu erstellen"
 
 msgctxt "field:sale.handle.shipment.exception.ask,domain_moves:"
 msgid "Domain Moves"
@@ -148,7 +144,7 @@ msgstr "ID"
 
 msgctxt "field:sale.handle.shipment.exception.ask,recreate_moves:"
 msgid "Recreate Moves"
-msgstr "Bewegungen nachbilden"
+msgstr "Lagerbewegungen neu erstellen"
 
 msgctxt "field:sale.line,amount:"
 msgid "Amount"
@@ -168,7 +164,7 @@ msgstr "Lieferdatum"
 
 msgctxt "field:sale.line,description:"
 msgid "Description"
-msgstr "Bezeichnung"
+msgstr "Beschreibung"
 
 msgctxt "field:sale.line,from_location:"
 msgid "From Location"
@@ -200,7 +196,7 @@ msgstr "Ignorierte Bewegungen"
 
 msgctxt "field:sale.line,moves_recreated:"
 msgid "Recreated Moves"
-msgstr "Nachgebildete Bewegungen"
+msgstr "Neu erstellte Lagerbewegungen"
 
 msgctxt "field:sale.line,note:"
 msgid "Note"
@@ -248,7 +244,7 @@ msgstr "Einheit"
 
 msgctxt "field:sale.line,unit_digits:"
 msgid "Unit Digits"
-msgstr "Anzahl Stellen"
+msgstr "Nachkommastellen"
 
 msgctxt "field:sale.line,unit_price:"
 msgid "Unit Price"
@@ -280,7 +276,7 @@ msgstr "ID"
 
 msgctxt "field:sale.line-account.tax,line:"
 msgid "Sale Line"
-msgstr "Position Verkauf"
+msgstr "Verkaufsposition"
 
 msgctxt "field:sale.line-account.tax,rec_name:"
 msgid "Name"
@@ -320,7 +316,7 @@ msgstr "Name"
 
 msgctxt "field:sale.line-ignored-stock.move,sale_line:"
 msgid "Sale Line"
-msgstr "Position Verkauf"
+msgstr "Verkaufsposition"
 
 msgctxt "field:sale.line-ignored-stock.move,write_date:"
 msgid "Write Date"
@@ -352,7 +348,7 @@ msgstr "Name"
 
 msgctxt "field:sale.line-recreated-stock.move,sale_line:"
 msgid "Sale Line"
-msgstr "Position Verkauf"
+msgstr "Verkaufsposition"
 
 msgctxt "field:sale.line-recreated-stock.move,write_date:"
 msgid "Write Date"
@@ -388,7 +384,7 @@ msgstr "Währung"
 
 msgctxt "field:sale.sale,currency_digits:"
 msgid "Currency Digits"
-msgstr "Währung (signifikante Stellen)"
+msgstr "Nachkommastellen Währung"
 
 msgctxt "field:sale.sale,description:"
 msgid "Description"
@@ -420,7 +416,7 @@ msgstr "Ignorierte Rechnungen"
 
 msgctxt "field:sale.sale,invoices_recreated:"
 msgid "Recreated Invoices"
-msgstr "Nachgebildete Rechnungen"
+msgstr "Neu erstellte Rechnungen"
 
 msgctxt "field:sale.sale,lines:"
 msgid "Lines"
@@ -514,38 +510,6 @@ msgctxt "field:sale.sale,write_uid:"
 msgid "Write User"
 msgstr "Letzte Änderung durch"
 
-msgctxt "field:sale.sale-account.invoice,create_date:"
-msgid "Create Date"
-msgstr "Erstellungsdatum"
-
-msgctxt "field:sale.sale-account.invoice,create_uid:"
-msgid "Create User"
-msgstr "Erstellt durch"
-
-msgctxt "field:sale.sale-account.invoice,id:"
-msgid "ID"
-msgstr "ID"
-
-msgctxt "field:sale.sale-account.invoice,invoice:"
-msgid "Invoice"
-msgstr "Rechnung"
-
-msgctxt "field:sale.sale-account.invoice,rec_name:"
-msgid "Name"
-msgstr "Name"
-
-msgctxt "field:sale.sale-account.invoice,sale:"
-msgid "Sale"
-msgstr "Verkauf"
-
-msgctxt "field:sale.sale-account.invoice,write_date:"
-msgid "Write Date"
-msgstr "Zuletzt geändert"
-
-msgctxt "field:sale.sale-account.invoice,write_uid:"
-msgid "Write User"
-msgstr "Letzte Änderung durch"
-
 msgctxt "field:sale.sale-ignored-account.invoice,create_date:"
 msgid "Create Date"
 msgstr "Erstellungsdatum"
@@ -626,7 +590,7 @@ msgctxt "help:sale.handle.invoice.exception.ask,recreate_invoices:"
 msgid ""
 "The selected invoices will be recreated. The other ones will be ignored."
 msgstr ""
-"Die ausgewählten Rechnungen werden nachgebildet. Alle anderen werden "
+"Die ausgewählten Rechnungen werden neu erstellt. Alle anderen werden "
 "ignoriert."
 
 msgctxt "model:ir.action,name:act_invoice_form"
@@ -746,7 +710,7 @@ msgstr "Einstellungen Verkauf"
 
 msgctxt "model:sale.handle.invoice.exception.ask,name:"
 msgid "Handle Invoice Exception"
-msgstr "Rechnungsvorbehalt Nachfrage"
+msgstr "Nachfrage Rechnungsvorbehalt"
 
 msgctxt "model:sale.handle.shipment.exception.ask,name:"
 msgid "Handle Shipment Exception"
@@ -754,19 +718,19 @@ msgstr "Nachfrage Liefervorbehalt"
 
 msgctxt "model:sale.line,name:"
 msgid "Sale Line"
-msgstr "Verkauf Position"
+msgstr "Verkaufsposition"
 
 msgctxt "model:sale.line-account.tax,name:"
 msgid "Sale Line - Tax"
-msgstr "Verkauf Position - Steuer"
+msgstr "Verkaufsposition - Steuer"
 
 msgctxt "model:sale.line-ignored-stock.move,name:"
 msgid "Sale Line - Ignored Move"
-msgstr "Verkauf Position - Bewegung Ignoriert"
+msgstr "Verkaufsposition - Bewegung Ignoriert"
 
 msgctxt "model:sale.line-recreated-stock.move,name:"
 msgid "Sale Line - Recreated Move"
-msgstr "Verkauf Position - Bewegung Nachgebildet"
+msgstr "Verkaufsposition - Neu erstellte Lagerbewegung"
 
 msgctxt "model:sale.return_sale.start,name:"
 msgid "Return Sale"
@@ -776,17 +740,13 @@ msgctxt "model:sale.sale,name:"
 msgid "Sale"
 msgstr "Verkauf"
 
-msgctxt "model:sale.sale-account.invoice,name:"
-msgid "Sale - Invoice"
-msgstr "Verkauf - Rechnung"
-
 msgctxt "model:sale.sale-ignored-account.invoice,name:"
 msgid "Sale - Ignored Invoice"
 msgstr "Verkauf - Rechnung Ignoriert"
 
 msgctxt "model:sale.sale-recreated-account.invoice,name:"
 msgid "Sale - Recreated Invoice"
-msgstr "Verkauf - Rechnung Nachgebildet"
+msgstr "Verkauf - Neu erstellte Rechnung"
 
 msgctxt "odt:sale.sale:"
 msgid "Amount"
@@ -798,11 +758,11 @@ msgstr "Datum:"
 
 msgctxt "odt:sale.sale:"
 msgid "Description"
-msgstr "Bezeichnung"
+msgstr "Beschreibung"
 
 msgctxt "odt:sale.sale:"
 msgid "Description:"
-msgstr "Bezeichnung:"
+msgstr "Beschreibung:"
 
 msgctxt "odt:sale.sale:"
 msgid "Draft Sale Order"
@@ -834,15 +794,15 @@ msgstr "Steuern"
 
 msgctxt "odt:sale.sale:"
 msgid "Taxes:"
-msgstr "Steuern:"
+msgstr "Steuern"
 
 msgctxt "odt:sale.sale:"
 msgid "Total (excl. taxes):"
-msgstr "Netto:"
+msgstr "Netto"
 
 msgctxt "odt:sale.sale:"
 msgid "Total:"
-msgstr "Gesamt:"
+msgstr "Gesamt"
 
 msgctxt "odt:sale.sale:"
 msgid "Unit Price"
@@ -854,7 +814,7 @@ msgstr "USt-ID-Nr.:"
 
 msgctxt "odt:sale.sale:"
 msgid "VAT:"
-msgstr "USt:"
+msgstr "USt-ID-Nr:"
 
 msgctxt "selection:account.invoice,sale_exception_state:"
 msgid ""
@@ -866,7 +826,7 @@ msgstr "Ignoriert"
 
 msgctxt "selection:account.invoice,sale_exception_state:"
 msgid "Recreated"
-msgstr "Nachgebildet"
+msgstr "Neu erstellt"
 
 msgctxt "selection:sale.configuration,sale_invoice_method:"
 msgid "Manual"
@@ -998,7 +958,7 @@ msgstr "Ignoriert"
 
 msgctxt "selection:stock.move,sale_exception_state:"
 msgid "Recreated"
-msgstr "Nachgebildet"
+msgstr "Neu erstellt"
 
 msgctxt "view:party.party:"
 msgid "Sale"
@@ -1018,7 +978,7 @@ msgstr "Einstellungen Verkauf"
 
 msgctxt "view:sale.handle.invoice.exception.ask:"
 msgid "Choose invoices to recreate"
-msgstr "Rechnungen zum Nachbilden auswählen"
+msgstr "Rechnungen zur Neuerstellung auswählen"
 
 msgctxt "view:sale.handle.invoice.exception.ask:"
 msgid "Handle Invoice Exception"
@@ -1026,7 +986,7 @@ msgstr "Rechnungsvorbehalt bearbeiten"
 
 msgctxt "view:sale.handle.shipment.exception.ask:"
 msgid "Choose move to recreate"
-msgstr "Bewegungen zum Nachbilden auswählen"
+msgstr "Lagerbewegungen zur Neuerstellung auswählen"
 
 msgctxt "view:sale.handle.shipment.exception.ask:"
 msgid "Handle shipment Exception"
@@ -1042,11 +1002,11 @@ msgstr "Notizen"
 
 msgctxt "view:sale.line:"
 msgid "Sale Line"
-msgstr "Position Verkauf"
+msgstr "Verkaufsposition"
 
 msgctxt "view:sale.line:"
 msgid "Sale Lines"
-msgstr "Positionen Verkauf"
+msgstr "Verkaufspositionen"
 
 msgctxt "view:sale.return_sale.start:"
 msgid "Are you sure to return those/this sale(s)?"
diff --git a/locale/es_AR.po b/locale/es_AR.po
index a73bc7d..fe9b838 100644
--- a/locale/es_AR.po
+++ b/locale/es_AR.po
@@ -3,10 +3,6 @@ msgid ""
 msgstr "Content-Type: text/plain; charset=utf-8\n"
 
 msgctxt "error:account.invoice:"
-msgid "You can not delete invoices that come from a sale."
-msgstr "No puede eliminar facturas generadas desde ventas."
-
-msgctxt "error:account.invoice:"
 msgid "You cannot reset to draft an invoice generated by a sale."
 msgstr "No puede restablecer a borrador una factura generada por una venta."
 
@@ -92,7 +88,7 @@ msgstr "ID"
 
 msgctxt "field:sale.configuration,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:sale.configuration,sale_invoice_method:"
 msgid "Sale Invoice Method"
@@ -502,38 +498,6 @@ msgctxt "field:sale.sale,write_uid:"
 msgid "Write User"
 msgstr "Usuario modificación"
 
-msgctxt "field:sale.sale-account.invoice,create_date:"
-msgid "Create Date"
-msgstr "Fecha creación"
-
-msgctxt "field:sale.sale-account.invoice,create_uid:"
-msgid "Create User"
-msgstr "Usuario creación"
-
-msgctxt "field:sale.sale-account.invoice,id:"
-msgid "ID"
-msgstr "ID"
-
-msgctxt "field:sale.sale-account.invoice,invoice:"
-msgid "Invoice"
-msgstr "Factura"
-
-msgctxt "field:sale.sale-account.invoice,rec_name:"
-msgid "Name"
-msgstr "Nombre"
-
-msgctxt "field:sale.sale-account.invoice,sale:"
-msgid "Sale"
-msgstr "Venta"
-
-msgctxt "field:sale.sale-account.invoice,write_date:"
-msgid "Write Date"
-msgstr "Fecha modificación"
-
-msgctxt "field:sale.sale-account.invoice,write_uid:"
-msgid "Write User"
-msgstr "Usuario modificación"
-
 msgctxt "field:sale.sale-ignored-account.invoice,create_date:"
 msgid "Create Date"
 msgstr "Fecha creación"
@@ -763,10 +727,6 @@ msgctxt "model:sale.sale,name:"
 msgid "Sale"
 msgstr "Venta"
 
-msgctxt "model:sale.sale-account.invoice,name:"
-msgid "Sale - Invoice"
-msgstr "Venta - Factura"
-
 msgctxt "model:sale.sale-ignored-account.invoice,name:"
 msgid "Sale - Ignored Invoice"
 msgstr "Venta - Factura ignorada"
diff --git a/locale/es_CO.po b/locale/es_CO.po
index 74e8c95..cff685c 100644
--- a/locale/es_CO.po
+++ b/locale/es_CO.po
@@ -3,12 +3,8 @@ msgid ""
 msgstr "Content-Type: text/plain; charset=utf-8\n"
 
 msgctxt "error:account.invoice:"
-msgid "You can not delete invoices that come from a sale."
-msgstr "No puede eliminar facturas que provienen de una venta."
-
-msgctxt "error:account.invoice:"
 msgid "You cannot reset to draft an invoice generated by a sale."
-msgstr "No puede restablecer un movimiento a borrador generado por una venta."
+msgstr "No puede devolver un movimiento a borrador generado por una venta."
 
 msgctxt "error:sale.line:"
 msgid "Product \"%(product)s\" of sale %(sale)s misses a revenue account."
@@ -55,11 +51,11 @@ msgstr "El depósito debe ser definido en la cotización para la venta \"%s\"."
 
 msgctxt "error:stock.shipment.out.return:"
 msgid "You cannot reset to draft a move generated by a sale."
-msgstr "No puede restablecer un movimiento a borrador generado por una venta."
+msgstr "No puede devolver un movimiento a borrador generado por una venta."
 
 msgctxt "error:stock.shipment.out:"
 msgid "You cannot reset to draft a move generated by a sale."
-msgstr "No puede restablecer un movimiento a borrador generado por una venta."
+msgstr "No puede devolver un movimiento a borrador generado por una venta."
 
 msgctxt "field:account.invoice,sale_exception_state:"
 msgid "Exception State"
@@ -239,7 +235,7 @@ msgstr "Unidad"
 
 msgctxt "field:sale.line,unit_digits:"
 msgid "Unit Digits"
-msgstr "Dígitos de la unidad"
+msgstr "Decimales de Unidad"
 
 msgctxt "field:sale.line,unit_price:"
 msgid "Unit Price"
@@ -379,7 +375,7 @@ msgstr "Moneda"
 
 msgctxt "field:sale.sale,currency_digits:"
 msgid "Currency Digits"
-msgstr "Decimales de la Moneda"
+msgstr "Decimales de Moneda"
 
 msgctxt "field:sale.sale,description:"
 msgid "Description"
@@ -483,15 +479,15 @@ msgstr "Total"
 
 msgctxt "field:sale.sale,total_amount_cache:"
 msgid "Total Tax"
-msgstr "Total impuestos"
+msgstr "Total Impuestos"
 
 msgctxt "field:sale.sale,untaxed_amount:"
 msgid "Untaxed"
-msgstr "Base sin Impuesto"
+msgstr "Base Sin Impuesto"
 
 msgctxt "field:sale.sale,untaxed_amount_cache:"
 msgid "Untaxed Cache"
-msgstr "Base sin Impuesto Precalculada"
+msgstr "Cache de Base Sin Impuesto"
 
 msgctxt "field:sale.sale,warehouse:"
 msgid "Warehouse"
@@ -505,38 +501,6 @@ msgctxt "field:sale.sale,write_uid:"
 msgid "Write User"
 msgstr "Modificado por Usuario"
 
-msgctxt "field:sale.sale-account.invoice,create_date:"
-msgid "Create Date"
-msgstr "Fecha de Creación"
-
-msgctxt "field:sale.sale-account.invoice,create_uid:"
-msgid "Create User"
-msgstr "Creado por Usuario"
-
-msgctxt "field:sale.sale-account.invoice,id:"
-msgid "ID"
-msgstr "ID"
-
-msgctxt "field:sale.sale-account.invoice,invoice:"
-msgid "Invoice"
-msgstr "Factura"
-
-msgctxt "field:sale.sale-account.invoice,rec_name:"
-msgid "Name"
-msgstr "Nombre"
-
-msgctxt "field:sale.sale-account.invoice,sale:"
-msgid "Sale"
-msgstr "Venta"
-
-msgctxt "field:sale.sale-account.invoice,write_date:"
-msgid "Write Date"
-msgstr "Fecha de Modificación"
-
-msgctxt "field:sale.sale-account.invoice,write_uid:"
-msgid "Write User"
-msgstr "Modificado por Usuario"
-
 msgctxt "field:sale.sale-ignored-account.invoice,create_date:"
 msgid "Create Date"
 msgstr "Fecha de Creación"
@@ -766,10 +730,6 @@ msgctxt "model:sale.sale,name:"
 msgid "Sale"
 msgstr "Venta"
 
-msgctxt "model:sale.sale-account.invoice,name:"
-msgid "Sale - Invoice"
-msgstr "Venta - Factura"
-
 msgctxt "model:sale.sale-ignored-account.invoice,name:"
 msgid "Sale - Ignored Invoice"
 msgstr "Venta - Factura Ignorada"
@@ -828,7 +788,7 @@ msgstr "Impuestos:"
 
 msgctxt "odt:sale.sale:"
 msgid "Total (excl. taxes):"
-msgstr "Base sin impuesto:"
+msgstr "Base (sin impuestos):"
 
 msgctxt "odt:sale.sale:"
 msgid "Total:"
@@ -840,7 +800,7 @@ msgstr "Precio Unitario"
 
 msgctxt "odt:sale.sale:"
 msgid "VAT Number:"
-msgstr "Número NIT:"
+msgstr "Número Identificación:"
 
 msgctxt "odt:sale.sale:"
 msgid "VAT:"
@@ -924,7 +884,7 @@ msgstr "Pagada"
 
 msgctxt "selection:sale.sale,invoice_state:"
 msgid "Waiting"
-msgstr "En espera"
+msgstr "En Espera"
 
 msgctxt "selection:sale.sale,shipment_method:"
 msgid "Manual"
@@ -952,11 +912,11 @@ msgstr "Enviada"
 
 msgctxt "selection:sale.sale,shipment_state:"
 msgid "Waiting"
-msgstr "En espera"
+msgstr "En Espera"
 
 msgctxt "selection:sale.sale,state:"
 msgid "Canceled"
-msgstr "Cancelada"
+msgstr "Anulada"
 
 msgctxt "selection:sale.sale,state:"
 msgid "Confirmed"
@@ -972,7 +932,7 @@ msgstr "Borrador"
 
 msgctxt "selection:sale.sale,state:"
 msgid "Processing"
-msgstr "En proceso"
+msgstr "En Proceso"
 
 msgctxt "selection:sale.sale,state:"
 msgid "Quotation"
@@ -1008,7 +968,7 @@ msgstr "Configuración Venta"
 
 msgctxt "view:sale.handle.invoice.exception.ask:"
 msgid "Choose invoices to recreate"
-msgstr "Seleccione facturas a recrear"
+msgstr "Seleccione facturas a rehacer"
 
 msgctxt "view:sale.handle.invoice.exception.ask:"
 msgid "Handle Invoice Exception"
@@ -1016,7 +976,7 @@ msgstr "Gestionar Excepción de Factura"
 
 msgctxt "view:sale.handle.shipment.exception.ask:"
 msgid "Choose move to recreate"
-msgstr "Seleccione movimientos a recrear"
+msgstr "Seleccione movimientos a rehacer"
 
 msgctxt "view:sale.handle.shipment.exception.ask:"
 msgid "Handle shipment Exception"
diff --git a/locale/es_ES.po b/locale/es_ES.po
index 734a961..6e5724c 100644
--- a/locale/es_ES.po
+++ b/locale/es_ES.po
@@ -3,10 +3,6 @@ msgid ""
 msgstr "Content-Type: text/plain; charset=utf-8\n"
 
 msgctxt "error:account.invoice:"
-msgid "You can not delete invoices that come from a sale."
-msgstr "No puede borrar facturas generadas desde ventas."
-
-msgctxt "error:account.invoice:"
 msgid "You cannot reset to draft an invoice generated by a sale."
 msgstr "No puede restaurar a borrador una factura generada por una venta."
 
@@ -28,7 +24,7 @@ msgstr "Falta la propiedad \"Cuenta a cobrar\" por defecto de las ventas."
 msgctxt "error:sale.sale:"
 msgid "Invalid combination of shipment and invoicing methods on sale \"%s\"."
 msgstr ""
-"Combinación de métodos de envío y facturación en la venta \"%s\" no es "
+"La combinación de métodos de envío y facturación en la venta \"%s\" no es "
 "correcta."
 
 msgctxt "error:sale.sale:"
@@ -177,7 +173,7 @@ msgstr "Movimientos realizados"
 
 msgctxt "field:sale.line,move_exception:"
 msgid "Moves Exception"
-msgstr "Exepción de movimientos"
+msgstr "Excepción de movimientos"
 
 msgctxt "field:sale.line,moves:"
 msgid "Moves"
@@ -503,38 +499,6 @@ msgctxt "field:sale.sale,write_uid:"
 msgid "Write User"
 msgstr "Usuario modificación"
 
-msgctxt "field:sale.sale-account.invoice,create_date:"
-msgid "Create Date"
-msgstr "Fecha creación"
-
-msgctxt "field:sale.sale-account.invoice,create_uid:"
-msgid "Create User"
-msgstr "Usuario creación"
-
-msgctxt "field:sale.sale-account.invoice,id:"
-msgid "ID"
-msgstr "ID"
-
-msgctxt "field:sale.sale-account.invoice,invoice:"
-msgid "Invoice"
-msgstr "Factura"
-
-msgctxt "field:sale.sale-account.invoice,rec_name:"
-msgid "Name"
-msgstr "Nombre"
-
-msgctxt "field:sale.sale-account.invoice,sale:"
-msgid "Sale"
-msgstr "Venta"
-
-msgctxt "field:sale.sale-account.invoice,write_date:"
-msgid "Write Date"
-msgstr "Fecha modificación"
-
-msgctxt "field:sale.sale-account.invoice,write_uid:"
-msgid "Write User"
-msgstr "Usuario modificación"
-
 msgctxt "field:sale.sale-ignored-account.invoice,create_date:"
 msgid "Create Date"
 msgstr "Fecha creación"
@@ -764,10 +728,6 @@ msgctxt "model:sale.sale,name:"
 msgid "Sale"
 msgstr "Venta"
 
-msgctxt "model:sale.sale-account.invoice,name:"
-msgid "Sale - Invoice"
-msgstr "Venta - Factura"
-
 msgctxt "model:sale.sale-ignored-account.invoice,name:"
 msgid "Sale - Ignored Invoice"
 msgstr "Venta - Factura ignorada"
@@ -962,7 +922,7 @@ msgstr "Confirmada"
 
 msgctxt "selection:sale.sale,state:"
 msgid "Done"
-msgstr "Realizado"
+msgstr "Realizada"
 
 msgctxt "selection:sale.sale,state:"
 msgid "Draft"
@@ -1038,7 +998,7 @@ msgstr "Líneas de venta"
 
 msgctxt "view:sale.return_sale.start:"
 msgid "Are you sure to return those/this sale(s)?"
-msgstr "¿Está seguro de devolver estos pedidos?"
+msgstr "¿Está seguro de devolver estos pedidos de venta?"
 
 msgctxt "view:sale.return_sale.start:"
 msgid "Return Sale"
@@ -1118,4 +1078,4 @@ msgstr "Cancelar"
 
 msgctxt "wizard_button:sale.return_sale,start,return_:"
 msgid "Return"
-msgstr "Devover"
+msgstr "Devolver"
diff --git a/locale/fr_FR.po b/locale/fr_FR.po
index 05a24a8..526b8a5 100644
--- a/locale/fr_FR.po
+++ b/locale/fr_FR.po
@@ -3,10 +3,6 @@ msgid ""
 msgstr "Content-Type: text/plain; charset=utf-8\n"
 
 msgctxt "error:account.invoice:"
-msgid "You can not delete invoices that come from a sale."
-msgstr "Vous ne pouvez supprimer une facture qui provient d'une vente."
-
-msgctxt "error:account.invoice:"
 msgid "You cannot reset to draft an invoice generated by a sale."
 msgstr "Vous ne pouvez pas réinitialiser une facture générée par une vente."
 
@@ -39,7 +35,7 @@ msgid ""
 "\"%s\"."
 msgstr ""
 "Les adresses de facturation et de livraison doivent être définies pour le "
-"devis \"%s\"."
+"devis de vente \"%s\"."
 
 msgctxt "error:sale.sale:"
 msgid "It misses an \"Account Receivable\" on the party \"%s\"."
@@ -47,7 +43,7 @@ msgstr "Le compte à recevoir est manquant sur le tiers \"%s\"."
 
 msgctxt "error:sale.sale:"
 msgid "Sale \"%s\" must be cancelled before deletion."
-msgstr "La vente \"%s\" doit être annulée avant de pouvoir être supprimée."
+msgstr "La vente \"%s\" doit être annulée avant suppression."
 
 msgctxt "error:sale.sale:"
 msgid "Warehouse must be defined for the quotation of sale \"%s\"."
@@ -399,7 +395,7 @@ msgstr "Méthode de facturation"
 
 msgctxt "field:sale.sale,invoice_state:"
 msgid "Invoice State"
-msgstr "État de la factutre"
+msgstr "État de la facture"
 
 msgctxt "field:sale.sale,invoices:"
 msgid "Invoices"
@@ -505,38 +501,6 @@ msgctxt "field:sale.sale,write_uid:"
 msgid "Write User"
 msgstr "Mis à jour par"
 
-msgctxt "field:sale.sale-account.invoice,create_date:"
-msgid "Create Date"
-msgstr "Date de création"
-
-msgctxt "field:sale.sale-account.invoice,create_uid:"
-msgid "Create User"
-msgstr "Créé par"
-
-msgctxt "field:sale.sale-account.invoice,id:"
-msgid "ID"
-msgstr "ID"
-
-msgctxt "field:sale.sale-account.invoice,invoice:"
-msgid "Invoice"
-msgstr "Facture"
-
-msgctxt "field:sale.sale-account.invoice,rec_name:"
-msgid "Name"
-msgstr "Nom"
-
-msgctxt "field:sale.sale-account.invoice,sale:"
-msgid "Sale"
-msgstr "Vente"
-
-msgctxt "field:sale.sale-account.invoice,write_date:"
-msgid "Write Date"
-msgstr "Date de mise à jour"
-
-msgctxt "field:sale.sale-account.invoice,write_uid:"
-msgid "Write User"
-msgstr "Mis à jour par"
-
 msgctxt "field:sale.sale-ignored-account.invoice,create_date:"
 msgid "Create Date"
 msgstr "Date de création"
@@ -766,10 +730,6 @@ msgctxt "model:sale.sale,name:"
 msgid "Sale"
 msgstr "Vente"
 
-msgctxt "model:sale.sale-account.invoice,name:"
-msgid "Sale - Invoice"
-msgstr "Vente - Facture"
-
 msgctxt "model:sale.sale-ignored-account.invoice,name:"
 msgid "Sale - Ignored Invoice"
 msgstr "Vente - Facture Ignorée"
diff --git a/locale/nl_NL.po b/locale/nl_NL.po
index eb161f8..e638807 100644
--- a/locale/nl_NL.po
+++ b/locale/nl_NL.po
@@ -3,10 +3,6 @@ msgid ""
 msgstr "Content-Type: text/plain; charset=utf-8\n"
 
 msgctxt "error:account.invoice:"
-msgid "You can not delete invoices that come from a sale."
-msgstr ""
-
-msgctxt "error:account.invoice:"
 msgid "You cannot reset to draft an invoice generated by a sale."
 msgstr ""
 "U kunt een factuur die uit verkoop voortkomt niet terug zetten naar concept."
@@ -502,38 +498,6 @@ msgctxt "field:sale.sale,write_uid:"
 msgid "Write User"
 msgstr ""
 
-msgctxt "field:sale.sale-account.invoice,create_date:"
-msgid "Create Date"
-msgstr ""
-
-msgctxt "field:sale.sale-account.invoice,create_uid:"
-msgid "Create User"
-msgstr ""
-
-msgctxt "field:sale.sale-account.invoice,id:"
-msgid "ID"
-msgstr ""
-
-msgctxt "field:sale.sale-account.invoice,invoice:"
-msgid "Invoice"
-msgstr "Factuur"
-
-msgctxt "field:sale.sale-account.invoice,rec_name:"
-msgid "Name"
-msgstr "Naam"
-
-msgctxt "field:sale.sale-account.invoice,sale:"
-msgid "Sale"
-msgstr "Verkoop"
-
-msgctxt "field:sale.sale-account.invoice,write_date:"
-msgid "Write Date"
-msgstr ""
-
-msgctxt "field:sale.sale-account.invoice,write_uid:"
-msgid "Write User"
-msgstr ""
-
 msgctxt "field:sale.sale-ignored-account.invoice,create_date:"
 msgid "Create Date"
 msgstr ""
@@ -777,10 +741,6 @@ msgctxt "model:sale.sale,name:"
 msgid "Sale"
 msgstr "Verkoop"
 
-msgctxt "model:sale.sale-account.invoice,name:"
-msgid "Sale - Invoice"
-msgstr "Verkoop - factuur"
-
 msgctxt "model:sale.sale-ignored-account.invoice,name:"
 msgid "Sale - Ignored Invoice"
 msgstr "Verkoop - genereerde factuur"
diff --git a/locale/ru_RU.po b/locale/ru_RU.po
index 14f83eb..0e62430 100644
--- a/locale/ru_RU.po
+++ b/locale/ru_RU.po
@@ -3,10 +3,6 @@ msgid ""
 msgstr "Content-Type: text/plain; charset=utf-8\n"
 
 msgctxt "error:account.invoice:"
-msgid "You can not delete invoices that come from a sale."
-msgstr "Вы не можете удалить инвойсы которые созданы от продажи."
-
-msgctxt "error:account.invoice:"
 msgid "You cannot reset to draft an invoice generated by a sale."
 msgstr "Нельзя отметить как черновой инвойс, который создан продажей."
 
@@ -503,38 +499,6 @@ msgctxt "field:sale.sale,write_uid:"
 msgid "Write User"
 msgstr "Изменено пользователем"
 
-msgctxt "field:sale.sale-account.invoice,create_date:"
-msgid "Create Date"
-msgstr "Дата создания"
-
-msgctxt "field:sale.sale-account.invoice,create_uid:"
-msgid "Create User"
-msgstr "Создано пользователем"
-
-msgctxt "field:sale.sale-account.invoice,id:"
-msgid "ID"
-msgstr "ID"
-
-msgctxt "field:sale.sale-account.invoice,invoice:"
-msgid "Invoice"
-msgstr "Инвойс"
-
-msgctxt "field:sale.sale-account.invoice,rec_name:"
-msgid "Name"
-msgstr "Наименование"
-
-msgctxt "field:sale.sale-account.invoice,sale:"
-msgid "Sale"
-msgstr "Продажа"
-
-msgctxt "field:sale.sale-account.invoice,write_date:"
-msgid "Write Date"
-msgstr "Дата изменения"
-
-msgctxt "field:sale.sale-account.invoice,write_uid:"
-msgid "Write User"
-msgstr "Изменено пользователем"
-
 msgctxt "field:sale.sale-ignored-account.invoice,create_date:"
 msgid "Create Date"
 msgstr "Дата создания"
@@ -768,10 +732,6 @@ msgctxt "model:sale.sale,name:"
 msgid "Sale"
 msgstr "Продажа"
 
-msgctxt "model:sale.sale-account.invoice,name:"
-msgid "Sale - Invoice"
-msgstr "Продажи - Инвойсы"
-
 msgctxt "model:sale.sale-ignored-account.invoice,name:"
 msgid "Sale - Ignored Invoice"
 msgstr "Продажи - Игнорируемые инвойсы"
diff --git a/locale/sl_SI.po b/locale/sl_SI.po
index 143a84f..4a0922d 100644
--- a/locale/sl_SI.po
+++ b/locale/sl_SI.po
@@ -3,14 +3,10 @@ msgid ""
 msgstr "Content-Type: text/plain; charset=utf-8\n"
 
 msgctxt "error:account.invoice:"
-msgid "You can not delete invoices that come from a sale."
-msgstr "Računov, ustvarjenih iz prodajnih nalogov, ni možno brisati."
-
-msgctxt "error:account.invoice:"
 msgid "You cannot reset to draft an invoice generated by a sale."
 msgstr ""
 "Računa, ustvarjenega iz prodajnega naloga, ni možno prestaviti v stanje "
-"osnutka."
+"priprave."
 
 msgctxt "error:sale.line:"
 msgid "Product \"%(product)s\" of sale %(sale)s misses a revenue account."
@@ -44,7 +40,7 @@ msgstr "Naslov plačnika in prejemnika sta obvezna pri prodajni ponudbi \"%s\"."
 
 msgctxt "error:sale.sale:"
 msgid "It misses an \"Account Receivable\" on the party \"%s\"."
-msgstr "Stranki \"%s\" manjka terjatveni konto."
+msgstr "Partnerju \"%s\" manjka terjatveni konto."
 
 msgctxt "error:sale.sale:"
 msgid "Sale \"%s\" must be cancelled before deletion."
@@ -58,13 +54,13 @@ msgctxt "error:stock.shipment.out.return:"
 msgid "You cannot reset to draft a move generated by a sale."
 msgstr ""
 "Prometa, ustvarjenega iz prodajnega naloga, ni možno prestaviti v stanje "
-"osnutka."
+"priprave."
 
 msgctxt "error:stock.shipment.out:"
 msgid "You cannot reset to draft a move generated by a sale."
 msgstr ""
 "Prometa, ustvarjenega iz prodajnega naloga, ni možno prestaviti v stanje "
-"osnutka."
+"priprave."
 
 msgctxt "field:account.invoice,sale_exception_state:"
 msgid "Exception State"
@@ -368,7 +364,7 @@ msgstr "Opomba"
 
 msgctxt "field:sale.sale,company:"
 msgid "Company"
-msgstr "Podjetje"
+msgstr "Družba"
 
 msgctxt "field:sale.sale,create_date:"
 msgid "Create Date"
@@ -428,11 +424,11 @@ msgstr "Promet"
 
 msgctxt "field:sale.sale,party:"
 msgid "Party"
-msgstr "Stranka"
+msgstr "Partner"
 
 msgctxt "field:sale.sale,party_lang:"
 msgid "Party Language"
-msgstr "Jezik stranke"
+msgstr "Jezik partnerja"
 
 msgctxt "field:sale.sale,payment_term:"
 msgid "Payment Term"
@@ -510,38 +506,6 @@ msgctxt "field:sale.sale,write_uid:"
 msgid "Write User"
 msgstr "Zapisal"
 
-msgctxt "field:sale.sale-account.invoice,create_date:"
-msgid "Create Date"
-msgstr "Ustvarjeno"
-
-msgctxt "field:sale.sale-account.invoice,create_uid:"
-msgid "Create User"
-msgstr "Ustvaril"
-
-msgctxt "field:sale.sale-account.invoice,id:"
-msgid "ID"
-msgstr "ID"
-
-msgctxt "field:sale.sale-account.invoice,invoice:"
-msgid "Invoice"
-msgstr "Račun"
-
-msgctxt "field:sale.sale-account.invoice,rec_name:"
-msgid "Name"
-msgstr "Ime"
-
-msgctxt "field:sale.sale-account.invoice,sale:"
-msgid "Sale"
-msgstr "Prodajni nalog"
-
-msgctxt "field:sale.sale-account.invoice,write_date:"
-msgid "Write Date"
-msgstr "Zapisano"
-
-msgctxt "field:sale.sale-account.invoice,write_uid:"
-msgid "Write User"
-msgstr "Zapisal"
-
 msgctxt "field:sale.sale-ignored-account.invoice,create_date:"
 msgid "Create Date"
 msgstr "Ustvarjeno"
@@ -629,7 +593,7 @@ msgstr "Računi"
 
 msgctxt "model:ir.action,name:act_open_customer"
 msgid "Parties associated to Sales"
-msgstr "Stranke, povezane s prodajo"
+msgstr "Partnerji, povezani s prodajo"
 
 msgctxt "model:ir.action,name:act_return_form"
 msgid "Returns"
@@ -686,7 +650,7 @@ msgstr "Potrjeno"
 
 msgctxt "model:ir.action.act_window.domain,name:act_sale_form_domain_draft"
 msgid "Draft"
-msgstr "Osnutek"
+msgstr "Priprava"
 
 msgctxt ""
 "model:ir.action.act_window.domain,name:act_sale_form_domain_processing"
@@ -712,7 +676,7 @@ msgstr "Nastavitve"
 
 msgctxt "model:ir.ui.menu,name:menu_customer"
 msgid "Parties associated to Sales"
-msgstr "Stranke pri prodaji"
+msgstr "Partnerji pri prodaji"
 
 msgctxt "model:ir.ui.menu,name:menu_sale"
 msgid "Sales"
@@ -770,10 +734,6 @@ msgctxt "model:sale.sale,name:"
 msgid "Sale"
 msgstr "Prodajni nalog"
 
-msgctxt "model:sale.sale-account.invoice,name:"
-msgid "Sale - Invoice"
-msgstr "Prodajni nalog - Račun"
-
 msgctxt "model:sale.sale-ignored-account.invoice,name:"
 msgid "Sale - Ignored Invoice"
 msgstr "Prodajni nalog - Prezrt račun"
@@ -972,7 +932,7 @@ msgstr "Zaključeno"
 
 msgctxt "selection:sale.sale,state:"
 msgid "Draft"
-msgstr "Osnutek"
+msgstr "V pripravi"
 
 msgctxt "selection:sale.sale,state:"
 msgid "Processing"
@@ -1060,7 +1020,7 @@ msgstr "Potrditev"
 
 msgctxt "view:sale.sale:"
 msgid "Draft"
-msgstr "Osnutek"
+msgstr "Priprava"
 
 msgctxt "view:sale.sale:"
 msgid "Handle Invoice Exception"
diff --git a/party.xml b/party.xml
index 7004e6b..67e6d3c 100644
--- a/party.xml
+++ b/party.xml
@@ -6,7 +6,7 @@ this repository contains the full copyright notices and license terms. -->
         <record model="ir.action.act_window" id="act_sale_form2">
             <field name="name">Sales</field>
             <field name="res_model">sale.sale</field>
-            <field name="domain">[("party", "=", Eval('active_id'))]</field>
+            <field name="domain">[('party', 'in', Eval('active_ids'))]</field>
         </record>
         <record model="ir.action.keyword"
                 id="act_open_sale_keyword1">
diff --git a/product.py b/product.py
index d771b7d..5bea238 100644
--- a/product.py
+++ b/product.py
@@ -24,7 +24,6 @@ class Template:
         domain=[
             ('category', '=', Eval('default_uom_category')),
             ],
-        on_change_with=['default_uom', 'sale_uom', 'salable'],
         depends=['active', 'salable', 'default_uom_category'])
     delivery_time = fields.Integer('Delivery Time', states={
             'readonly': ~Eval('active', True),
@@ -52,6 +51,7 @@ class Template:
     def default_delivery_time():
         return 0
 
+    @fields.depends('default_uom', 'sale_uom', 'salable')
     def on_change_with_sale_uom(self):
         if self.default_uom:
             if self.sale_uom:
diff --git a/sale.py b/sale.py
index a5da83d..74bf951 100644
--- a/sale.py
+++ b/sale.py
@@ -16,7 +16,7 @@ from trytond.pyson import If, Eval, Bool, PYSONEncoder, Id
 from trytond.transaction import Transaction
 from trytond.pool import Pool, PoolMeta
 
-__all__ = ['Sale', 'SaleInvoice', 'SaleIgnoredInvoice', 'SaleRecreatedInvoice',
+__all__ = ['Sale', 'SaleIgnoredInvoice', 'SaleRecreatedInvoice',
     'SaleLine', 'SaleLineTax', 'SaleLineIgnoredMove',
     'SaleLineRecreatedMove', 'SaleReport', 'OpenCustomer',
     'HandleShipmentExceptionAsk', 'HandleShipmentException',
@@ -68,17 +68,19 @@ class Sale(Workflow, ModelSQL, ModelView):
     party = fields.Many2One('party.party', 'Party', required=True, select=True,
         states={
             'readonly': Eval('state') != 'draft',
-            }, on_change=['party', 'payment_term'],
+            },
         depends=['state'])
-    party_lang = fields.Function(fields.Char('Party Language',
-            on_change_with=['party']), 'on_change_with_party_lang')
+    party_lang = fields.Function(fields.Char('Party Language'),
+        'on_change_with_party_lang')
     invoice_address = fields.Many2One('party.address', 'Invoice Address',
         domain=[('party', '=', Eval('party'))], states={
             'readonly': Eval('state') != 'draft',
+            'required': ~Eval('state').in_(['draft', 'quotation', 'cancel']),
             }, depends=['state', 'party'])
     shipment_address = fields.Many2One('party.address', 'Shipment Address',
         domain=[('party', '=', Eval('party'))], states={
             'readonly': Eval('state') != 'draft',
+            'required': ~Eval('state').in_(['draft', 'quotation', 'cancel']),
             },
         depends=['party', 'state'])
     warehouse = fields.Many2One('stock.location', 'Warehouse',
@@ -92,11 +94,11 @@ class Sale(Workflow, ModelSQL, ModelView):
                 (Eval('lines', [0]) & Eval('currency', 0)),
             },
         depends=['state'])
-    currency_digits = fields.Function(fields.Integer('Currency Digits',
-            on_change_with=['currency']), 'on_change_with_currency_digits')
+    currency_digits = fields.Function(fields.Integer('Currency Digits'),
+        'on_change_with_currency_digits')
     lines = fields.One2Many('sale.line', 'sale', 'Lines', states={
             'readonly': Eval('state') != 'draft',
-            }, on_change=['lines', 'currency', 'party'],
+            },
         depends=['party', 'state'])
     comment = fields.Text('Comment')
     untaxed_amount = fields.Function(fields.Numeric('Untaxed',
@@ -135,8 +137,8 @@ class Sale(Workflow, ModelSQL, ModelView):
             ('paid', 'Paid'),
             ('exception', 'Exception'),
             ], 'Invoice State', readonly=True, required=True)
-    invoices = fields.Many2Many('sale.sale-account.invoice',
-            'sale', 'invoice', 'Invoices', readonly=True)
+    invoices = fields.Function(fields.One2Many('account.invoice', None,
+            'Invoices'), 'get_invoices', searcher='search_invoices')
     invoices_ignored = fields.Many2Many('sale.sale-ignored-account.invoice',
             'sale', 'invoice', 'Ignored Invoices', readonly=True)
     invoices_recreated = fields.Many2Many(
@@ -188,6 +190,8 @@ class Sale(Workflow, ModelSQL, ModelView):
                 ('quotation', 'confirmed'),
                 ('confirmed', 'processing'),
                 ('processing', 'processing'),
+                ('processing', 'done'),
+                ('done', 'processing'),
                 ('draft', 'cancel'),
                 ('quotation', 'cancel'),
                 ('quotation', 'draft'),
@@ -368,6 +372,7 @@ class Sale(Workflow, ModelSQL, ModelView):
     def default_shipment_state():
         return 'none'
 
+    @fields.depends('party', 'payment_term')
     def on_change_party(self):
         invoice_address = None
         shipment_address = None
@@ -396,6 +401,7 @@ class Sale(Workflow, ModelSQL, ModelView):
             changes['payment_term'] = self.default_payment_term()
         return changes
 
+    @fields.depends('currency')
     def on_change_with_currency_digits(self, name=None):
         if self.currency:
             return self.currency.digits
@@ -407,12 +413,14 @@ class Sale(Workflow, ModelSQL, ModelView):
             res['language'] = self.party.lang.code
         return res
 
+    @fields.depends('party')
     def on_change_with_party_lang(self, name=None):
         Config = Pool().get('ir.configuration')
         if self.party and self.party.lang:
             return self.party.lang.code
         return Config.get_language()
 
+    @fields.depends('lines', 'currency', 'party')
     def on_change_lines(self):
         pool = Pool()
         Tax = pool.get('account.tax')
@@ -429,12 +437,13 @@ class Sale(Workflow, ModelSQL, ModelView):
             for line in self.lines:
                 if getattr(line, 'type', 'line') != 'line':
                     continue
-                res['untaxed_amount'] += line.amount or Decimal(0)
+                res['untaxed_amount'] += (getattr(line, 'amount', None)
+                    or Decimal(0))
                 tax_list = ()
                 with Transaction().set_context(self.get_tax_context()):
                     tax_list = Tax.compute(getattr(line, 'taxes', []),
-                        line.unit_price or Decimal('0.0'),
-                        line.quantity or 0.0)
+                        getattr(line, 'unit_price', None) or Decimal('0.0'),
+                        getattr(line, 'quantity', None) or 0.0)
                 for tax in tax_list:
                     key, val = Invoice._compute_tax(tax, 'out_invoice')
                     if not key in taxes:
@@ -480,21 +489,31 @@ class Sale(Workflow, ModelSQL, ModelView):
         tax_amount = {}
         total_amount = {}
 
+        if {'tax_amount', 'total_amount'} & set(names):
+            compute_taxes = True
+        else:
+            compute_taxes = False
+        # Sort cached first and re-instanciate to optimize cache management
+        sales = sorted(sales, key=lambda s: s.state in cls._states_cached,
+            reverse=True)
+        sales = cls.browse(sales)
         for sale in sales:
             if (sale.state in cls._states_cached
                     and sale.untaxed_amount_cache is not None
                     and sale.tax_amount_cache is not None
                     and sale.total_amount_cache is not None):
                 untaxed_amount[sale.id] = sale.untaxed_amount_cache
-                tax_amount[sale.id] = sale.tax_amount_cache
-                total_amount[sale.id] = sale.total_amount_cache
+                if compute_taxes:
+                    tax_amount[sale.id] = sale.tax_amount_cache
+                    total_amount[sale.id] = sale.total_amount_cache
             else:
                 untaxed_amount[sale.id] = sum(
                     (line.amount for line in sale.lines
                         if line.type == 'line'), _ZERO)
-                tax_amount[sale.id] = sale.get_tax_amount()
-                total_amount[sale.id] = (
-                    untaxed_amount[sale.id] + tax_amount[sale.id])
+                if compute_taxes:
+                    tax_amount[sale.id] = sale.get_tax_amount()
+                    total_amount[sale.id] = (
+                        untaxed_amount[sale.id] + tax_amount[sale.id])
 
         result = {
             'untaxed_amount': untaxed_amount,
@@ -506,6 +525,18 @@ class Sale(Workflow, ModelSQL, ModelView):
                 del result[key]
         return result
 
+    def get_invoices(self, name):
+        invoices = set()
+        for line in self.lines:
+            for invoice_line in line.invoice_lines:
+                if invoice_line.invoice:
+                    invoices.add(invoice_line.invoice.id)
+        return list(invoices)
+
+    @classmethod
+    def search_invoices(cls, name, clause):
+        return [('lines.invoice_lines.invoice.id',) + tuple(clause[1:])]
+
     def get_invoice_state(self):
         '''
         Return the invoice state for the sale.
@@ -623,7 +654,6 @@ class Sale(Workflow, ModelSQL, ModelView):
         default['state'] = 'draft'
         default['reference'] = None
         default['invoice_state'] = 'none'
-        default['invoices'] = None
         default['invoices_ignored'] = None
         default['shipment_state'] = 'none'
         default.setdefault('sale_date', None)
@@ -743,10 +773,6 @@ class Sale(Workflow, ModelSQL, ModelView):
 
         with Transaction().set_user(0, set_context=True):
             Invoice.update_taxes([invoice])
-
-        self.write([self], {
-                'invoices': [('add', [invoice.id])],
-                })
         return invoice
 
     def _get_move_sale_line(self, shipment_type):
@@ -778,6 +804,15 @@ class Sale(Workflow, ModelSQL, ModelView):
 
     _group_return_key = _group_shipment_key
 
+    def _get_shipment_sale(self, Shipment, key):
+        values = {
+            'customer': self.party.id,
+            'delivery_address': self.shipment_address.id,
+            'company': self.company.id,
+            }
+        values.update(dict(key))
+        return Shipment(**values)
+
     def create_shipment(self, shipment_type):
         '''
         Create and return shipments of type shipment_type
@@ -801,15 +836,10 @@ class Sale(Workflow, ModelSQL, ModelView):
 
         shipments = []
         for key, grouped_moves in groupby(moves, key=keyfunc):
-            values = {
-                'customer': self.party.id,
-                'delivery_address': self.shipment_address.id,
-                'company': self.company.id,
-                }
-            values.update(dict(key))
             with Transaction().set_user(0, set_context=True):
-                shipment = Shipment(**values)
-                shipment.moves = [x[1] for x in grouped_moves]
+                shipment = self._get_shipment_sale(Shipment, key)
+                shipment.moves = (list(getattr(shipment, 'moves', []))
+                    + [x[1] for x in grouped_moves])
                 shipment.save()
                 shipments.append(shipment)
         if shipment_type == 'out':
@@ -819,9 +849,11 @@ class Sale(Workflow, ModelSQL, ModelView):
 
     def is_done(self):
         return ((self.invoice_state == 'paid'
-                or self.invoice_method == 'manual')
+                or self.invoice_state == 'none')
             and (self.shipment_state == 'sent'
-                or self.shipment_method == 'manual'))
+                or self.shipment_state == 'none'
+                or all(l.product.type == 'service'
+                    for l in self.lines if l.product)))
 
     @classmethod
     def delete(cls, sales):
@@ -870,12 +902,22 @@ class Sale(Workflow, ModelSQL, ModelView):
         pass
 
     @classmethod
-    @ModelView.button
     @Workflow.transition('processing')
+    def proceed(cls, sales):
+        pass
+
+    @classmethod
+    @Workflow.transition('done')
+    def do(cls, sales):
+        pass
+
+    @classmethod
+    @ModelView.button
     def process(cls, sales):
         done = []
+        process = []
         for sale in sales:
-            if sale.state in ('done', 'cancel'):
+            if sale.state not in ('confirmed', 'processing', 'done'):
                 continue
             sale.create_invoice('out_invoice')
             sale.create_invoice('out_credit_note')
@@ -884,21 +926,14 @@ class Sale(Workflow, ModelSQL, ModelView):
             sale.create_shipment('return')
             sale.set_shipment_state()
             if sale.is_done():
-                done.append(sale)
+                if sale.state != 'done':
+                    done.append(sale)
+            elif sale.state != 'processing':
+                process.append(sale)
+        if process:
+            cls.proceed(process)
         if done:
-            cls.write(done, {
-                    'state': 'done',
-                    })
-
-
-class SaleInvoice(ModelSQL):
-    'Sale - Invoice'
-    __name__ = 'sale.sale-account.invoice'
-    _table = 'sale_invoices_rel'
-    sale = fields.Many2One('sale.sale', 'Sale', ondelete='CASCADE',
-        select=True, required=True)
-    invoice = fields.Many2One('account.invoice', 'Invoice',
-            ondelete='RESTRICT', select=True, required=True)
+            cls.do(done)
 
 
 class SaleIgnoredInvoice(ModelSQL):
@@ -940,9 +975,7 @@ class SaleLine(ModelSQL, ModelView):
             'invisible': Eval('type') != 'line',
             'required': Eval('type') == 'line',
             'readonly': ~Eval('_parent_sale', {}),
-            }, on_change=['product', 'quantity', 'unit',
-            '_parent_sale.currency', '_parent_sale.party',
-            '_parent_sale.sale_date'],
+            },
         depends=['type', 'unit_digits'])
     unit = fields.Many2One('product.uom', 'Unit',
             states={
@@ -955,20 +988,15 @@ class SaleLine(ModelSQL, ModelView):
                 ('category', '=', Eval('product_uom_category')),
                 ('category', '!=', -1)),
             ],
-        on_change=['product', 'quantity', 'unit', '_parent_sale.currency',
-            '_parent_sale.party'],
         depends=['product', 'type', 'product_uom_category'])
-    unit_digits = fields.Function(fields.Integer('Unit Digits',
-        on_change_with=['unit']), 'on_change_with_unit_digits')
+    unit_digits = fields.Function(fields.Integer('Unit Digits'),
+        'on_change_with_unit_digits')
     product = fields.Many2One('product.product', 'Product',
         domain=[('salable', '=', True)],
         states={
             'invisible': Eval('type') != 'line',
             'readonly': ~Eval('_parent_sale', {}),
             },
-        on_change=['product', 'unit', 'quantity', 'description',
-            '_parent_sale.party', '_parent_sale.currency',
-            '_parent_sale.sale_date'],
         context={
             'locations': If(Bool(Eval('_parent_sale', {}).get('warehouse')),
                 [Eval('_parent_sale', {}).get('warehouse', 0)], []),
@@ -976,8 +1004,7 @@ class SaleLine(ModelSQL, ModelView):
             'stock_skip_warehouse': True,
             }, depends=['type'])
     product_uom_category = fields.Function(
-        fields.Many2One('product.uom.category', 'Product Uom Category',
-            on_change_with=['product']),
+        fields.Many2One('product.uom.category', 'Product Uom Category'),
         'on_change_with_product_uom_category')
     unit_price = fields.Numeric('Unit Price', digits=(16, 4),
         states={
@@ -989,8 +1016,7 @@ class SaleLine(ModelSQL, ModelView):
             states={
                 'invisible': ~Eval('type').in_(['line', 'subtotal']),
                 'readonly': ~Eval('_parent_sale'),
-                }, on_change_with=['type', 'quantity', 'unit_price', 'unit',
-                '_parent_sale.currency'],
+                },
             depends=['type']), 'get_amount')
     description = fields.Text('Description', size=None, required=True)
     note = fields.Text('Note')
@@ -1019,7 +1045,6 @@ class SaleLine(ModelSQL, ModelView):
     to_location = fields.Function(fields.Many2One('stock.location',
             'To Location'), 'get_to_location')
     delivery_date = fields.Function(fields.Date('Delivery Date',
-            on_change_with=['product', 'quantity', '_parent_sale.sale_date'],
             states={
                 'invisible': ((Eval('type') != 'line')
                     | (If(Bool(Eval('quantity')), Eval('quantity', 0), 0)
@@ -1073,6 +1098,7 @@ class SaleLine(ModelSQL, ModelView):
     def default_unit_digits():
         return 2
 
+    @fields.depends('unit')
     def on_change_with_unit_digits(self, name=None):
         if self.unit:
             return self.unit.digits
@@ -1129,6 +1155,9 @@ class SaleLine(ModelSQL, ModelView):
             context['uom'] = self.product.sale_uom.id
         return context
 
+    @fields.depends('product', 'unit', 'quantity', 'description',
+        '_parent_sale.party', '_parent_sale.currency',
+        '_parent_sale.sale_date')
     def on_change_product(self):
         Product = Pool().get('product.product')
 
@@ -1179,10 +1208,14 @@ class SaleLine(ModelSQL, ModelView):
         res['amount'] = self.on_change_with_amount()
         return res
 
+    @fields.depends('product')
     def on_change_with_product_uom_category(self, name=None):
         if self.product:
             return self.product.default_uom_category.id
 
+    @fields.depends('product', 'quantity', 'unit',
+        '_parent_sale.currency', '_parent_sale.party',
+        '_parent_sale.sale_date')
     def on_change_quantity(self):
         Product = Pool().get('product.product')
 
@@ -1199,9 +1232,14 @@ class SaleLine(ModelSQL, ModelView):
                     Decimal(1) / 10 ** self.__class__.unit_price.digits[1])
         return res
 
+    @fields.depends('product', 'quantity', 'unit',
+        '_parent_sale.currency', '_parent_sale.party',
+        '_parent_sale.sale_date')
     def on_change_unit(self):
         return self.on_change_quantity()
 
+    @fields.depends('type', 'quantity', 'unit_price', 'unit',
+        '_parent_sale.currency')
     def on_change_with_amount(self):
         if self.type == 'line':
             currency = self.sale.currency if self.sale else None
@@ -1246,6 +1284,7 @@ class SaleLine(ModelSQL, ModelView):
             if self.warehouse:
                 return self.warehouse.input_location.id
 
+    @fields.depends('product', 'quantity', '_parent_sale.sale_date')
     def on_change_with_delivery_date(self, name=None):
         if self.product and self.quantity > 0:
             date = self.sale.sale_date if self.sale else None
@@ -1282,16 +1321,21 @@ class SaleLine(ModelSQL, ModelView):
         if (invoice_type == 'out_invoice') != (self.quantity >= 0):
             return []
 
+        stock_moves = []
         if (self.sale.invoice_method == 'order'
                 or not self.product
                 or self.product.type == 'service'):
             quantity = abs(self.quantity)
+            stock_moves = self.moves
         else:
             quantity = 0.0
             for move in self.moves:
                 if move.state == 'done':
                     quantity += Uom.compute_qty(move.uom, move.quantity,
                         self.unit)
+                    if move.invoiced_quantity < move.quantity:
+                        stock_moves.append(move)
+        invoice_line.stock_moves = stock_moves
 
         skip_ids = set(l.id for i in self.sale.invoices_recreated
             for l in i.lines)
@@ -1341,7 +1385,7 @@ class SaleLine(ModelSQL, ModelView):
 
     def get_move(self, shipment_type):
         '''
-        Return moves for the sale line according ot shipment_type
+        Return moves for the sale line according to shipment_type
         '''
         pool = Pool()
         Uom = pool.get('product.uom')
@@ -1356,14 +1400,18 @@ class SaleLine(ModelSQL, ModelView):
         if (shipment_type == 'out') != (self.quantity >= 0):
             return
 
+        invoice_lines = []
         if self.sale.shipment_method == 'order':
             quantity = abs(self.quantity)
+            invoice_lines = self.invoice_lines
         else:
             quantity = 0.0
             for invoice_line in self.invoice_lines:
                 if invoice_line.invoice.state == 'paid':
                     quantity += Uom.compute_qty(invoice_line.unit,
                         invoice_line.quantity, self.unit)
+                    if invoice_line.moved_quantity < invoice_line.quantity:
+                        invoice_lines.append(invoice_line)
 
         skip_ids = set(x.id for x in self.moves_recreated)
         for move in self.moves:
@@ -1389,6 +1437,7 @@ class SaleLine(ModelSQL, ModelView):
         move.unit_price = self.unit_price
         move.currency = self.sale.currency
         move.planned_date = self.delivery_date
+        move.invoice_lines = invoice_lines
         move.origin = self
         return move
 
diff --git a/sale.xml b/sale.xml
index d67233d..41237b4 100644
--- a/sale.xml
+++ b/sale.xml
@@ -71,7 +71,7 @@ this repository contains the full copyright notices and license terms. -->
         <record model="ir.action.act_window" id="act_shipment_form">
             <field name="name">Shipments</field>
             <field name="res_model">stock.shipment.out</field>
-            <field name="domain">[("id", "in", Eval('shipments'))]</field>
+            <field name="domain">[('moves.sale', 'in', Eval('active_ids'))]</field>
         </record>
         <record model="ir.action.keyword"
                 id="act_open_shipment_keyword1">
@@ -82,7 +82,7 @@ this repository contains the full copyright notices and license terms. -->
         <record model="ir.action.act_window" id="act_return_form">
             <field name="name">Returns</field>
             <field name="res_model">stock.shipment.out.return</field>
-            <field name="domain">[("id", "in", Eval('shipment_returns'))]</field>
+            <field name="domain">[('moves.sale', 'in', Eval('active_ids'))]</field>
         </record>
         <record model="ir.action.keyword" id="act_open_shipment_return_keyword1">
             <field name="keyword">form_relate</field>
@@ -92,7 +92,7 @@ this repository contains the full copyright notices and license terms. -->
         <record model="ir.action.act_window" id="act_invoice_form">
             <field name="name">Invoices</field>
             <field name="res_model">account.invoice</field>
-            <field name="domain">[("id", "in", Eval('invoices'))]</field>
+            <field name="domain">[('lines.origin.sale.id', 'in', Eval('active_ids'), 'sale.line')]</field>
         </record>
         <record model="ir.action.keyword"
                 id="act_open_invoice_keyword1">
@@ -157,7 +157,7 @@ this repository contains the full copyright notices and license terms. -->
         <record model="ir.action.act_window" id="act_sale_invoice_relate">
             <field name="name">Sales</field>
             <field name="res_model">sale.sale</field>
-            <field name="domain">[('invoices', 'in', [Eval('id')])]</field>
+            <field name="domain">[('invoices', 'in', Eval('active_ids'))]</field>
         </record>
         <record model="ir.action.act_window.view"
             id="act_sale_invoice_relate_view1">
@@ -181,7 +181,7 @@ this repository contains the full copyright notices and license terms. -->
         <record model="ir.action.act_window" id="act_sale_shipments_relate">
             <field name="name">Sales</field>
             <field name="res_model">sale.sale</field>
-            <field name="domain">[('shipments', 'in', [Eval('id')])]</field>
+            <field name="domain">[('shipments', 'in', Eval('active_ids'))]</field>
         </record>
         <record model="ir.action.act_window.view"
             id="act_sale_shipments_relate_view1">
diff --git a/setup.py b/setup.py
index 31c4408..cc8fe8d 100644
--- a/setup.py
+++ b/setup.py
@@ -11,36 +11,53 @@ import ConfigParser
 def read(fname):
     return open(os.path.join(os.path.dirname(__file__), fname)).read()
 
+
+def get_require_version(name):
+    if minor_version % 2:
+        require = '%s >= %s.%s.dev0, < %s.%s'
+    else:
+        require = '%s >= %s.%s, < %s.%s'
+    require %= (name, major_version, minor_version,
+        major_version, minor_version + 1)
+    return require
+
 config = ConfigParser.ConfigParser()
 config.readfp(open('tryton.cfg'))
 info = dict(config.items('tryton'))
 for key in ('depends', 'extras_depend', 'xml'):
     if key in info:
         info[key] = info[key].strip().splitlines()
-major_version, minor_version, _ = info.get('version', '0.0.1').split('.', 2)
+version = info.get('version', '0.0.1')
+major_version, minor_version, _ = version.split('.', 2)
 major_version = int(major_version)
 minor_version = int(minor_version)
+name = 'trytond_sale'
+
+download_url = 'http://downloads.tryton.org/%s.%s/' % (
+    major_version, minor_version)
+if minor_version % 2:
+    version = '%s.%s.dev0' % (major_version, minor_version)
+    download_url = (
+        'hg+http://hg.tryton.org/modules/%s#egg=%s-%s' % (
+            name[8:], name, version))
 
 requires = ['python-sql']
 for dep in info.get('depends', []):
     if not re.match(r'(ir|res|webdav)(\W|$)', dep):
-        requires.append('trytond_%s >= %s.%s, < %s.%s' %
-            (dep, major_version, minor_version, major_version,
-                minor_version + 1))
-requires.append('trytond >= %s.%s, < %s.%s' %
-    (major_version, minor_version, major_version, minor_version + 1))
+        requires.append(get_require_version('trytond_%s' % dep))
+requires.append(get_require_version('trytond'))
 
-tests_require = ['proteus >= %s.%s, < %s.%s' %
-    (major_version, minor_version, major_version, minor_version + 1)]
+tests_require = [get_require_version('proteus')]
 
-setup(name='trytond_sale',
-    version=info.get('version', '0.0.1'),
+setup(name=name,
+    version=version,
     description='Tryton module for sale',
     long_description=read('README'),
     author='Tryton',
+    author_email='issue_tracker at tryton.org',
     url='http://www.tryton.org/',
-    download_url=("http://downloads.tryton.org/" +
-        info.get('version', '0.0.1').rsplit('.', 1)[0] + '/'),
+    download_url=download_url,
+    keywords='tryton sale',
     package_dir={'trytond.modules.sale': '.'},
     packages=[
         'trytond.modules.sale',
@@ -70,7 +87,6 @@ setup(name='trytond_sale',
         'Natural Language :: Slovenian',
         'Natural Language :: Spanish',
         'Operating System :: OS Independent',
-        'Programming Language :: Python :: 2.6',
         'Programming Language :: Python :: 2.7',
         'Topic :: Office/Business',
         'Topic :: Office/Business :: Financial :: Accounting',
diff --git a/stock.py b/stock.py
index 542cc76..f05264f 100644
--- a/stock.py
+++ b/stock.py
@@ -23,14 +23,17 @@ class ShipmentOut:
             })
 
     @classmethod
-    def write(cls, shipments, vals):
+    def write(cls, *args):
         pool = Pool()
         Sale = pool.get('sale.sale')
         SaleLine = pool.get('sale.line')
 
-        super(ShipmentOut, cls).write(shipments, vals)
+        super(ShipmentOut, cls).write(*args)
 
-        if 'state' in vals and vals['state'] in ('done', 'cancel'):
+        actions = iter(args)
+        for shipments, values in zip(actions, actions):
+            if values.get('state') not in ('done', 'cancel'):
+                continue
             sales = []
             move_ids = []
             for shipment in shipments:
@@ -70,14 +73,17 @@ class ShipmentOutReturn:
             })
 
     @classmethod
-    def write(cls, shipments, vals):
+    def write(cls, *args):
         pool = Pool()
         Sale = pool.get('sale.sale')
         SaleLine = pool.get('sale.line')
 
-        super(ShipmentOutReturn, cls).write(shipments, vals)
+        super(ShipmentOutReturn, cls).write(*args)
 
-        if 'state' in vals and vals['state'] == 'received':
+        actions = iter(args)
+        for shipments, values in zip(actions, actions):
+            if values.get('state') != 'received':
+                continue
             sales = []
             move_ids = []
             for shipment in shipments:
@@ -143,6 +149,12 @@ class Move:
         models.append('sale.line')
         return models
 
+    @classmethod
+    def check_origin_types(cls):
+        types = super(Move, cls).check_origin_types()
+        types.add('customer')
+        return types
+
     def get_sale(self, name):
         SaleLine = Pool().get('sale.line')
         if isinstance(self.origin, SaleLine):
diff --git a/tests/scenario_sale.rst b/tests/scenario_sale.rst
index fa10185..1e4951a 100644
--- a/tests/scenario_sale.rst
+++ b/tests/scenario_sale.rst
@@ -27,11 +27,11 @@ Create company::
 
     >>> Currency = Model.get('currency.currency')
     >>> CurrencyRate = Model.get('currency.currency.rate')
-    >>> currencies = Currency.find([('code', '=', 'EUR')])
+    >>> currencies = Currency.find([('code', '=', 'USD')])
     >>> if not currencies:
-    ...     currency = Currency(name='Euro', symbol=u'€', code='EUR',
+    ...     currency = Currency(name='U.S. Dollar', symbol='$', code='USD',
     ...         rounding=Decimal('0.01'), mon_grouping='[3, 3, 0]',
-    ...         mon_decimal_point=',')
+    ...         mon_decimal_point='.', mon_thousands_sep=',')
     ...     currency.save()
     ...     CurrencyRate(date=today + relativedelta(month=1, day=1),
     ...         rate=Decimal('1.0'), currency=currency).save()
@@ -42,7 +42,7 @@ Create company::
     >>> company_config = Wizard('company.company.config')
     >>> company_config.execute('company')
     >>> company = company_config.form
-    >>> party = Party(name='B2CK')
+    >>> party = Party(name='Dunder Mifflin')
     >>> party.save()
     >>> company.party = party
     >>> company.currency = currency
@@ -112,6 +112,7 @@ Create chart of accounts::
 
     >>> AccountTemplate = Model.get('account.account.template')
     >>> Account = Model.get('account.account')
+    >>> Journal = Model.get('account.journal')
     >>> account_template, = AccountTemplate.find([('parent', '=', None)])
     >>> create_chart = Wizard('account.create_chart')
     >>> create_chart.execute('account')
@@ -137,6 +138,15 @@ Create chart of accounts::
     >>> create_chart.form.account_receivable = receivable
     >>> create_chart.form.account_payable = payable
     >>> create_chart.execute('create_properties')
+    >>> cash, = Account.find([
+    ...         ('kind', '=', 'other'),
+    ...         ('name', '=', 'Main Cash'),
+    ...         ('company', '=', company.id),
+    ...         ])
+    >>> cash_journal, = Journal.find([('type', '=', 'cash')])
+    >>> cash_journal.credit_account = cash
+    >>> cash_journal.debit_account = cash
+    >>> cash_journal.save()
 
 Create parties::
 
@@ -175,6 +185,21 @@ Create product::
     >>> product.template = template
     >>> product.save()
 
+    >>> service = Product()
+    >>> template = ProductTemplate()
+    >>> template.name = 'service'
+    >>> template.default_uom = unit
+    >>> template.type = 'service'
+    >>> template.salable = True
+    >>> template.list_price = Decimal('30')
+    >>> template.cost_price = Decimal('10')
+    >>> template.cost_price_method = 'fixed'
+    >>> template.account_expense = expense
+    >>> template.account_revenue = revenue
+    >>> template.save()
+    >>> service.template = template
+    >>> service.save()
+
 Create payment term::
 
     >>> PaymentTerm = Model.get('account.invoice.payment_term')
@@ -242,6 +267,21 @@ Sale 5 products::
     >>> shipment.origins == sale.rec_name
     True
 
+Invoice line must be linked to stock move::
+
+    >>> _, invoice_line1, invoice_line2 = sorted(invoice.lines,
+    ...     key=lambda l: l.quantity)
+    >>> stock_move1, stock_move2 = sorted(shipment.outgoing_moves,
+    ...     key=lambda m: m.quantity)
+    >>> invoice_line1.stock_moves == [stock_move1]
+    True
+    >>> stock_move1.invoice_lines == [invoice_line1]
+    True
+    >>> invoice_line2.stock_moves == [stock_move2]
+    True
+    >>> stock_move2.invoice_lines == [invoice_line2]
+    True
+
 Post invoice and check no new invoices::
 
 
@@ -284,10 +324,19 @@ Sale 5 products with an invoice method 'on shipment'::
     >>> len(sale.shipments), len(sale.shipment_returns), len(sale.invoices)
     (1, 0, 0)
 
-Validate Shipments::
+Not yet linked to invoice lines::
 
     >>> shipment, = sale.shipments
     >>> config.user = stock_user.id
+    >>> stock_move1, stock_move2 = sorted(shipment.outgoing_moves,
+    ...     key=lambda m: m.quantity)
+    >>> len(stock_move1.invoice_lines)
+    0
+    >>> len(stock_move2.invoice_lines)
+    0
+
+Validate Shipments::
+
     >>> ShipmentOut = Model.get('stock.shipment.out')
     >>> ShipmentOut.assign_try([shipment.id], config.context)
     True
@@ -298,18 +347,26 @@ Open customer invoice::
 
     >>> config.user = sale_user.id
     >>> sale.reload()
-    >>> Invoice = Model.get('account.invoice')
     >>> invoice, = sale.invoices
     >>> config.user = account_user.id
+    >>> Invoice = Model.get('account.invoice')
+    >>> invoice = Invoice(invoice.id)
     >>> invoice.type
     u'out_invoice'
-    >>> len(invoice.lines)
-    2
+    >>> invoice_line1, invoice_line2 = sorted(invoice.lines,
+    ...     key=lambda l: l.quantity)
     >>> for line in invoice.lines:
     ...     line.quantity = 1
     ...     line.save()
     >>> Invoice.post([invoice.id], config.context)
 
+Invoice lines must be linked to each stock moves::
+
+    >>> invoice_line1.stock_moves == [stock_move1]
+    True
+    >>> invoice_line2.stock_moves == [stock_move2]
+    True
+
 Check second invoices::
 
     >>> config.user = sale_user.id
@@ -319,6 +376,96 @@ Check second invoices::
     >>> sum(l.quantity for i in sale.invoices for l in i.lines)
     5.0
 
+Sale 5 products with shipment method 'on invoice'::
+
+    >>> config.user = sale_user.id
+    >>> sale = Sale()
+    >>> sale.party = customer
+    >>> sale.payment_term = payment_term
+    >>> sale.shipment_method = 'invoice'
+    >>> sale_line = sale.lines.new()
+    >>> sale_line.product = product
+    >>> sale_line.quantity = 5.0
+    >>> sale.click('quote')
+    >>> sale.click('confirm')
+    >>> sale.click('process')
+    >>> sale.state
+    u'processing'
+    >>> len(sale.shipments), len(sale.shipment_returns), len(sale.invoices)
+    (0, 0, 1)
+
+Not yet linked to stock moves::
+
+    >>> invoice, = sale.invoices
+    >>> config.user = account_user.id
+    >>> invoice_line, = invoice.lines
+    >>> len(invoice_line.stock_moves)
+    0
+
+Post and Pay Invoice for 4 products::
+
+    >>> Invoice = Model.get('account.invoice')
+    >>> invoice = Invoice(invoice.id)
+    >>> invoice_line, = invoice.lines
+    >>> invoice_line.quantity
+    5.0
+    >>> invoice_line.quantity = 4.0
+    >>> invoice.click('post')
+    >>> pay = Wizard('account.invoice.pay', [invoice])
+    >>> pay.form.journal = cash_journal
+    >>> pay.execute('choice')
+    >>> invoice.reload()
+    >>> invoice.state
+    u'paid'
+
+Invoice lines linked to 1 move::
+
+    >>> config.user = account_user.id
+    >>> invoice_line, = invoice.lines
+    >>> len(invoice_line.stock_moves)
+    1
+
+Stock moves must be linked to invoice line::
+
+    >>> config.user = sale_user.id
+    >>> sale.reload()
+    >>> shipment, = sale.shipments
+    >>> config.user = stock_user.id
+    >>> shipment = ShipmentOut(shipment.id)
+    >>> stock_move, = shipment.outgoing_moves
+    >>> stock_move.quantity
+    4.0
+    >>> stock_move.invoice_lines == [invoice_line]
+    True
+
+Ship 3 products::
+
+    >>> stock_inventory_move, = shipment.inventory_moves
+    >>> stock_inventory_move.quantity
+    4.0
+    >>> stock_inventory_move.quantity = 3.0
+    >>> shipment.click('assign_try')
+    True
+    >>> shipment.click('pack')
+    >>> shipment.click('done')
+    >>> shipment.state
+    u'done'
+
+New shipments created::
+
+    >>> config.user = sale_user.id
+    >>> sale.reload()
+    >>> len(sale.shipments)
+    2
+
+Invoice lines linked to new moves::
+
+    >>> config.user = account_user.id
+    >>> invoice.reload()
+    >>> invoice_line, = invoice.lines
+    >>> len(invoice_line.stock_moves)
+    2
+
 Create a Return::
 
     >>> config.user = sale_user.id
@@ -364,6 +511,7 @@ Open customer credit note::
     >>> return_.reload()
     >>> credit_note, = return_.invoices
     >>> config.user = account_user.id
+    >>> credit_note = Invoice(credit_note.id)
     >>> credit_note.type
     u'out_credit_note'
     >>> len(credit_note.lines)
@@ -430,6 +578,8 @@ Checking the invoice::
     >>> mix_invoice, mix_credit_note = sorted(mix.invoices,
     ...     key=attrgetter('type'), reverse=True)
     >>> config.user = account_user.id
+    >>> mix_invoice = Invoice(mix_invoice.id)
+    >>> mix_credit_note = Invoice(mix_credit_note.id)
     >>> mix_invoice.type, mix_credit_note.type
     (u'out_invoice', u'out_credit_note')
     >>> len(mix_invoice.lines), len(mix_credit_note.lines)
@@ -490,3 +640,42 @@ Checking Shipments::
     u'product'
     >>> move_shipment.quantity
     6.0
+
+Sale services::
+
+    >>> config.user = sale_user.id
+    >>> service_sale = Sale()
+    >>> service_sale.party = customer
+    >>> service_sale.payment_term = payment_term
+    >>> sale_line = service_sale.lines.new()
+    >>> sale_line.product = service
+    >>> sale_line.quantity = 1
+    >>> service_sale.save()
+    >>> service_sale.click('quote')
+    >>> service_sale.click('confirm')
+    >>> service_sale.click('process')
+    >>> service_sale.state
+    u'processing'
+    >>> service_invoice, = service_sale.invoices
+
+Pay the service invoice::
+
+    >>> config.user = account_user.id
+    >>> service_invoice.click('post')
+    >>> pay = Wizard('account.invoice.pay', [service_invoice])
+    >>> pay.form.journal = cash_journal
+    >>> pay.execute('choice')
+    >>> service_invoice.reload()
+    >>> service_invoice.state
+    u'paid'
+
+Check service sale states::
+
+    >>> config.user = sale_user.id
+    >>> service_sale.reload()
+    >>> service_sale.invoice_state
+    u'paid'
+    >>> service_sale.shipment_state
+    u'none'
+    >>> service_sale.state
+    u'done'
diff --git a/tests/test_sale.py b/tests/test_sale.py
index 0b897f7..733aeda 100644
--- a/tests/test_sale.py
+++ b/tests/test_sale.py
@@ -1,52 +1,26 @@
-#!/usr/bin/env python
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-
-import sys
-import os
-DIR = os.path.abspath(os.path.normpath(os.path.join(__file__,
-    '..', '..', '..', '..', '..', 'trytond')))
-if os.path.isdir(DIR):
-    sys.path.insert(0, os.path.dirname(DIR))
-
 import unittest
 import doctest
 import trytond.tests.test_tryton
-from trytond.tests.test_tryton import test_view, test_depends
-from trytond.backend.sqlite.database import Database as SQLiteDatabase
+from trytond.tests.test_tryton import test_view, test_depends, doctest_dropdb
 
 
 class SaleTestCase(unittest.TestCase):
-    '''
-    Test Sale module.
-    '''
+    'Test Sale module'
 
     def setUp(self):
         trytond.tests.test_tryton.install_module('sale')
 
     def test0005views(self):
-        '''
-        Test views.
-        '''
+        'Test views'
         test_view('sale')
 
     def test0006depends(self):
-        '''
-        Test depends.
-        '''
+        'Test depends'
         test_depends()
 
 
-def doctest_dropdb(test):
-    database = SQLiteDatabase().connect()
-    cursor = database.cursor(autocommit=True)
-    try:
-        database.drop(cursor, ':memory:')
-        cursor.commit()
-    finally:
-        cursor.close()
-
-
 def suite():
     suite = trytond.tests.test_tryton.suite()
     suite.addTests(unittest.TestLoader().loadTestsFromTestCase(SaleTestCase))
@@ -54,6 +28,3 @@ def suite():
             setUp=doctest_dropdb, tearDown=doctest_dropdb, encoding='utf-8',
             optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
     return suite
-
-if __name__ == '__main__':
-    unittest.TextTestRunner(verbosity=2).run(suite())
diff --git a/tryton.cfg b/tryton.cfg
index 8d98329..3af04bc 100644
--- a/tryton.cfg
+++ b/tryton.cfg
@@ -1,8 +1,9 @@
 [tryton]
-version=3.0.0
+version=3.2.0
 depends:
     account
     account_invoice
+    account_invoice_stock
     account_product
     company
     currency
diff --git a/trytond_sale.egg-info/PKG-INFO b/trytond_sale.egg-info/PKG-INFO
index fffdcc9..1e59235 100644
--- a/trytond_sale.egg-info/PKG-INFO
+++ b/trytond_sale.egg-info/PKG-INFO
@@ -1,12 +1,12 @@
 Metadata-Version: 1.1
 Name: trytond-sale
-Version: 3.0.0
+Version: 3.2.0
 Summary: Tryton module for sale
 Home-page: http://www.tryton.org/
 Author: Tryton
-Author-email: UNKNOWN
+Author-email: issue_tracker at tryton.org
 License: GPL-3
-Download-URL: http://downloads.tryton.org/3.0/
+Download-URL: http://downloads.tryton.org/3.2/
 Description: trytond_sale
         ============
         
@@ -43,6 +43,7 @@ Description: trytond_sale
         
           http://www.tryton.org/
         
+Keywords: tryton sale
 Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Plugins
@@ -62,7 +63,6 @@ Classifier: Natural Language :: Russian
 Classifier: Natural Language :: Slovenian
 Classifier: Natural Language :: Spanish
 Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Topic :: Office/Business
 Classifier: Topic :: Office/Business :: Financial :: Accounting
diff --git a/trytond_sale.egg-info/requires.txt b/trytond_sale.egg-info/requires.txt
index bc38aef..4fa7d4c 100644
--- a/trytond_sale.egg-info/requires.txt
+++ b/trytond_sale.egg-info/requires.txt
@@ -1,10 +1,11 @@
 python-sql
-trytond_account >= 3.0, < 3.1
-trytond_account_invoice >= 3.0, < 3.1
-trytond_account_product >= 3.0, < 3.1
-trytond_company >= 3.0, < 3.1
-trytond_currency >= 3.0, < 3.1
-trytond_party >= 3.0, < 3.1
-trytond_product >= 3.0, < 3.1
-trytond_stock >= 3.0, < 3.1
-trytond >= 3.0, < 3.1
\ No newline at end of file
+trytond_account >= 3.2, < 3.3
+trytond_account_invoice >= 3.2, < 3.3
+trytond_account_invoice_stock >= 3.2, < 3.3
+trytond_account_product >= 3.2, < 3.3
+trytond_company >= 3.2, < 3.3
+trytond_currency >= 3.2, < 3.3
+trytond_party >= 3.2, < 3.3
+trytond_product >= 3.2, < 3.3
+trytond_stock >= 3.2, < 3.3
+trytond >= 3.2, < 3.3
\ No newline at end of file
diff --git a/view/sale_tree.xml b/view/sale_tree.xml
index abdf83c..e4b7fc1 100644
--- a/view/sale_tree.xml
+++ b/view/sale_tree.xml
@@ -8,7 +8,6 @@ this repository contains the full copyright notices and license terms. -->
     <field name="warehouse"/>
     <field name="currency"/>
     <field name="untaxed_amount"/>
-    <field name="total_amount"/>
     <field name="state"/>
     <field name="invoice_state"/>
     <field name="shipment_state"/>
-- 
tryton-modules-sale



More information about the tryton-debian-vcs mailing list