diff --git a/base_company_extension/__manifest__.py b/base_company_extension/__manifest__.py
index 258f449..4db2e51 100644
--- a/base_company_extension/__manifest__.py
+++ b/base_company_extension/__manifest__.py
@@ -35,11 +35,11 @@ This module adds 2 fields on the Company :
* *Capital Amount*
-* *Legal Form* (technical name: title, configured as a related field of res.partner)
+* *Legal Form*
""",
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': ['base'],
'data': ['company_view.xml'],
- 'installable': False,
+ 'installable': True,
}
diff --git a/base_company_extension/company.py b/base_company_extension/company.py
index 6a2a10d..277728e 100644
--- a/base_company_extension/company.py
+++ b/base_company_extension/company.py
@@ -26,10 +26,10 @@ from openerp import models, fields
class ResCompany(models.Model):
_inherit = "res.company"
- capital_amount = fields.Integer(string='Capital Amount')
- title = fields.Many2one(
- 'res.partner.title', related='partner_id.title',
- string='Legal Form')
+ capital_amount = fields.Monetary(string='Capital Amount')
+ # in v9, title is only for contacts, not for companies
+ legal_type = fields.Char(
+ string="Legal Type", help="Type of Company, e.g. SARL, SAS, ...")
_sql_constraints = [(
'capital_amount_positive',
diff --git a/base_company_extension/company_view.xml b/base_company_extension/company_view.xml
index 1e3dfe0..1e96d6a 100644
--- a/base_company_extension/company_view.xml
+++ b/base_company_extension/company_view.xml
@@ -6,7 +6,7 @@
The licence is in the file __openerp__.py
-->
-
+
@@ -15,13 +15,12 @@
-
-
+
+
-
+
diff --git a/l10n_fr_infogreffe_connector/infogreffe.py b/l10n_fr_infogreffe_connector/infogreffe.py
index 1f7cf7e..0497335 100644
--- a/l10n_fr_infogreffe_connector/infogreffe.py
+++ b/l10n_fr_infogreffe_connector/infogreffe.py
@@ -22,7 +22,7 @@
#
##############################################################################
-from openerp.osv import orm, fields
+from openerp import models, fields
from openerp.tools.translate import _
import requests
from datetime import datetime
@@ -38,15 +38,13 @@ logger = logging.getLogger(__name__)
URL = 'https://www.infogreffe.fr'
-class res_partner(orm.Model):
+class res_partner(models.Model):
_inherit = 'res.partner'
- _columns = {
- 'infogreffe_date': fields.date('Date', readonly=True),
- 'infogreffe_turnover': fields.integer(u'Turnover (€)', readonly=True),
- 'infogreffe_profit': fields.integer(u'Profit (€)', readonly=True),
- 'infogreffe_headcount': fields.integer('Headcount', readonly=True),
- }
+ infogreffe_date = fields.Date(string='Date', readonly=True)
+ infogreffe_turnover = fields.Integer(string=u'Turnover (€)', readonly=True)
+ infogreffe_profit = fields.Integer(string=u'Profit (€)', readonly=True)
+ infogreffe_headcount = fields.Integer(string='Headcount', readonly=True)
def copy(self, cr, uid, id, default=None, context=None):
if default is None:
diff --git a/sale_from_private_stock/__init__.py b/sale_from_private_stock/__init__.py
new file mode 100644
index 0000000..cf27e76
--- /dev/null
+++ b/sale_from_private_stock/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import sale_private_stock
diff --git a/sale_from_private_stock/__manifest__.py b/sale_from_private_stock/__manifest__.py
new file mode 100644
index 0000000..3f8ab54
--- /dev/null
+++ b/sale_from_private_stock/__manifest__.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+# © 2016 Akretion (Alexis de Lattre )
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+{
+ 'name': 'Sale from Private Stock',
+ 'version': '10.0.1.0.0',
+ 'category': 'Sales Management',
+ 'license': 'AGPL-3',
+ 'summary': 'Sell from private stock',
+ 'description': '''
+Sale from Private Stock
+=======================
+
+This module is particularly useful for companies that lend stock to their distributors and when it's too heavy to create a new warehouse for each distributor.
+
+This module allows to define a private stock location on a customer.
+
+On a sale order, there is a new optional field to define the source stock location. On a sale order, if you select a customer that has a private stock location, this private stock location will be set as the source stock location of the sale order (but you can change it).
+
+
+This module has been written by Alexis de Lattre from Akretion
+.
+ ''',
+ 'author': 'Akretion',
+ 'depends': ['sale_stock'],
+ 'data': [
+ 'stock_view.xml',
+ 'stock_data.xml',
+ 'partner_view.xml',
+ ],
+ 'installable': True,
+}
diff --git a/sale_from_private_stock/partner_view.xml b/sale_from_private_stock/partner_view.xml
new file mode 100644
index 0000000..edd7e37
--- /dev/null
+++ b/sale_from_private_stock/partner_view.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+ sale_from_private_stock.partner.form
+ res.partner
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sale_from_private_stock/sale_private_stock.py b/sale_from_private_stock/sale_private_stock.py
new file mode 100644
index 0000000..8a612bf
--- /dev/null
+++ b/sale_from_private_stock/sale_private_stock.py
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+# © 2016 Akretion (Alexis de Lattre )
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from openerp import models, fields, api, _
+from openerp.exceptions import UserError
+
+
+class StockWarehouse(models.Model):
+ _inherit = 'stock.warehouse'
+
+ private_stock_out_type_id = fields.Many2one(
+ 'stock.picking.type', string='Private Stock Out Type')
+
+
+class ResPartner(models.Model):
+ _inherit = 'res.partner'
+
+ default_sale_route_id = fields.Many2one(
+ 'stock.location.route', string="Default Stock Location Route",
+ company_dependent=True,
+ domain=[('usage', '=', 'internal')],
+ help="Stock location route used by default in sale order lines"
+ "for this customer.")
+
+ @api.multi
+ def create_private_location_route(self):
+ self.ensure_one()
+ assert not self.default_sale_route_id,\
+ 'Already has a default_sale_route_id'
+ slo = self.env['stock.location']
+ swo = self.env['stock.warehouse']
+ pro = self.env['procurement.rule']
+ slro = self.env['stock.location.route']
+ company = self.env.user.company_id
+ warehouses = swo.search([
+ ('company_id', '=', company.id),
+ ('private_stock_out_type_id', '!=', False)])
+ if not warehouses:
+ raise UserError(_(
+ "No warehouse with a 'Private Stock Out Type' in the "
+ "company %s") % company.name)
+ warehouse = warehouses[0]
+ private_stock_loc = slo.create({
+ 'name': _('Private stock %s') % self.name,
+ 'location_id': warehouse.view_location_id.id,
+ 'usage': 'internal',
+ 'company_id': company.id,
+ })
+ rule = pro.create({
+ 'name': _('From private stock %s to customer') % self.name,
+ 'company_id': company.id,
+ 'warehouse_id': warehouse.id,
+ 'action': 'move',
+ 'location_id': self.property_stock_customer.id,
+ 'location_src_id': private_stock_loc.id,
+ 'procure_method': 'make_to_stock',
+ 'picking_type_id': warehouse.private_stock_out_type_id.id,
+ })
+
+ route = slro.create({
+ 'name': _('Take from %s') % self.name,
+ 'sequence': 1000,
+ 'pull_ids': [(6, 0, [rule.id])],
+ 'product_selectable': False,
+ 'product_categ_selectable': False,
+ 'warehouse_selectable': False,
+ 'sale_selectable': True,
+ })
+ self.default_sale_route_id = route.id
+
+
+class SaleOrderLine(models.Model):
+ _inherit = 'sale.order.line'
+
+ @api.onchange('product_id')
+ def _set_default_sale_route(self):
+ print "DO NOT EXECUTE"
+ commercial_partner = self.order_id.partner_id.commercial_partner_id
+ if commercial_partner.default_sale_route_id:
+ self.route_id = commercial_partner.default_sale_route_id
+
+ # TODO: check compat between warehouse and route ?
diff --git a/sale_from_private_stock/stock_data.xml b/sale_from_private_stock/stock_data.xml
new file mode 100644
index 0000000..ee140cb
--- /dev/null
+++ b/sale_from_private_stock/stock_data.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+ Picking OUT from private stock
+ stock.picking
+ OUT/PRIV/
+ 5
+
+
+
+
+ Delivery from private stock
+
+ outgoing
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sale_from_private_stock/stock_view.xml b/sale_from_private_stock/stock_view.xml
new file mode 100644
index 0000000..0681233
--- /dev/null
+++ b/sale_from_private_stock/stock_view.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+ sale_from_private_stock.warehouse.form
+ stock.warehouse
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sale_order_add_bom/__init__.py b/sale_order_add_bom/__init__.py
new file mode 100644
index 0000000..545b9e4
--- /dev/null
+++ b/sale_order_add_bom/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+
+from . import mrp_bom
+from . import wizard
diff --git a/sale_order_add_bom/__manifest__.py b/sale_order_add_bom/__manifest__.py
new file mode 100644
index 0000000..d35d5f6
--- /dev/null
+++ b/sale_order_add_bom/__manifest__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# © 2016 Akretion (Alexis de Lattre )
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+{
+ 'name': 'Sale Order Add Bom',
+ 'version': '9.0.1.0.0',
+ 'category': 'Sales Management',
+ 'license': 'AGPL-3',
+ 'summary': 'Wizard to select a bom from a sale order',
+ 'description': """
+This module adds a wizard *Add Kit* on the form view of a quotation that allows the user to select a 'kit' BOM: Odoo will automatically add the components of the kit as sale order lines.
+
+This module has been written by Alexis de Lattre from Akretion
+.
+ """,
+ 'author': 'Akretion',
+ 'website': 'http://www.akretion.com',
+ 'depends': ['sale', 'mrp'],
+ 'data': [
+ 'wizard/sale_add_phantom_bom_view.xml',
+ 'sale_view.xml',
+ ],
+ 'installable': True,
+}
diff --git a/sale_order_add_bom/mrp_bom.py b/sale_order_add_bom/mrp_bom.py
new file mode 100644
index 0000000..b2799b8
--- /dev/null
+++ b/sale_order_add_bom/mrp_bom.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# © 2016 Akretion (Alexis de Lattre )
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from openerp import models, fields
+
+
+class MrpBom(models.Model):
+ _inherit = 'mrp.bom'
+ _rec_name = 'product_id'
+
+ sale_ok = fields.Boolean(related='product_id.sale_ok', store=True)
diff --git a/sale_order_add_bom/sale_view.xml b/sale_order_add_bom/sale_view.xml
new file mode 100644
index 0000000..d38a017
--- /dev/null
+++ b/sale_order_add_bom/sale_view.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+ add.bom.sale.order.form
+ sale.order
+
+
+
+
+
+
+
+
diff --git a/sale_order_add_bom/wizard/__init__.py b/sale_order_add_bom/wizard/__init__.py
new file mode 100644
index 0000000..f473f0c
--- /dev/null
+++ b/sale_order_add_bom/wizard/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import sale_add_phantom_bom
diff --git a/sale_order_add_bom/wizard/sale_add_phantom_bom.py b/sale_order_add_bom/wizard/sale_add_phantom_bom.py
new file mode 100644
index 0000000..b451441
--- /dev/null
+++ b/sale_order_add_bom/wizard/sale_add_phantom_bom.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+# © 2016 Akretion (Alexis de Lattre )
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from openerp import models, fields, api, _
+from openerp.exceptions import UserError
+from openerp.tools import float_is_zero
+
+
+class SaleAddPhantomBom(models.TransientModel):
+ _name = 'sale.add.phantom.bom'
+ _description = 'Add Kit to Quotation'
+
+ @api.model
+ def _default_sale_id(self):
+ assert self._context.get('active_model') == 'sale.order'
+ return self.env['sale.order'].browse(self._context['active_id'])
+
+ bom_id = fields.Many2one(
+ 'mrp.bom', 'Kit', required=True,
+ domain=[('type', '=', 'phantom'), ('sale_ok', '=', True)])
+ qty = fields.Integer(
+ string='Number of Kits to Add', default=1, required=True)
+ # I can 't put the sale_id fields required=True because
+ # it may block the deletion of a sale order
+ sale_id = fields.Many2one(
+ 'sale.order', string='Quotation', default=_default_sale_id)
+
+ @api.model
+ def _prepare_sale_order_line(self, bom_line, sale_order, wizard_qty):
+ qty_in_product_uom = self.env['product.uom']._compute_qty_obj(
+ bom_line.product_uom,
+ bom_line.product_qty,
+ bom_line.product_id.uom_id)
+ vals = {
+ 'product_id': bom_line.product_id.id,
+ 'product_uom_qty': qty_in_product_uom * wizard_qty,
+ 'order_id': sale_order.id,
+ }
+ return vals
+
+ @api.multi
+ def add(self):
+ self.ensure_one()
+ assert self.sale_id, 'No related sale_id'
+ if self.qty < 1:
+ raise UserError(_(
+ "The number of kits to add must be 1 or superior"))
+ assert self.bom_id.type == 'phantom', 'The BOM is not a kit'
+ if not self.bom_id.bom_line_ids:
+ raise UserError(_("The selected kit is empty !"))
+ prec = self.env['decimal.precision'].precision_get(
+ 'Product Unit of Measure')
+ today = fields.Date.context_today(self)
+ solo = self.env['sale.order.line']
+ for line in self.bom_id.bom_line_ids:
+ if float_is_zero(line.product_qty, precision_digits=prec):
+ continue
+ if line.date_start and line.date_start > today:
+ continue
+ if line.date_stop and line.date_stop < today:
+ continue
+ # The onchange is played in the inherit of the create()
+ # of sale order line in the 'sale' module
+ # TODO: if needed, we could increment existing order lines
+ # with the same product instead of always creating new lines
+ vals = self._prepare_sale_order_line(line, self.sale_id, self.qty)
+ solo.create(vals)
+ return True
diff --git a/sale_order_add_bom/wizard/sale_add_phantom_bom_view.xml b/sale_order_add_bom/wizard/sale_add_phantom_bom_view.xml
new file mode 100644
index 0000000..dff5025
--- /dev/null
+++ b/sale_order_add_bom/wizard/sale_add_phantom_bom_view.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+ sale.add.phantom.bom.form
+ sale.add.phantom.bom
+
+
+
+
+
+
+ Add Kit
+ sale.add.phantom.bom
+ form
+ new
+
+
+
+