diff --git a/contract_sale_generation/__init__.py b/contract_sale_generation/__init__.py
index a9e337226..0650744f6 100644
--- a/contract_sale_generation/__init__.py
+++ b/contract_sale_generation/__init__.py
@@ -1,2 +1 @@
-
from . import models
diff --git a/contract_sale_generation/__manifest__.py b/contract_sale_generation/__manifest__.py
index 2dd931b40..053a25239 100644
--- a/contract_sale_generation/__manifest__.py
+++ b/contract_sale_generation/__manifest__.py
@@ -4,18 +4,17 @@
{
- 'name': 'Contracts Management - Recurring Sales',
- 'version': '12.0.1.0.2',
- 'category': 'Contract Management',
- 'license': 'AGPL-3',
- 'author': "PESOL, "
- "Odoo Community Association (OCA)",
- 'website': 'https://github.com/oca/contract',
- 'depends': ['contract', 'sale'],
- 'data': [
- 'data/contract_cron.xml',
- 'views/contract.xml',
- 'views/contract_template.xml',
+ "name": "Contracts Management - Recurring Sales",
+ "version": "14.0.1.0.0",
+ "category": "Contract Management",
+ "license": "AGPL-3",
+ "author": "PESOL, " "Odoo Community Association (OCA)",
+ "website": "https://github.com/OCA/contract",
+ "depends": ["contract", "sale"],
+ "data": [
+ "data/contract_cron.xml",
+ "views/contract.xml",
+ "views/contract_template.xml",
],
- 'installable': True,
+ "installable": True,
}
diff --git a/contract_sale_generation/data/contract_cron.xml b/contract_sale_generation/data/contract_cron.xml
index 841f52971..c2f206dc1 100644
--- a/contract_sale_generation/data/contract_cron.xml
+++ b/contract_sale_generation/data/contract_cron.xml
@@ -1,8 +1,7 @@
-
-
+
Generate Recurring sales from Contracts
-
+
code
model.cron_recurring_create_sale()
@@ -11,5 +10,4 @@
-1
-
diff --git a/contract_sale_generation/models/abstract_contract.py b/contract_sale_generation/models/abstract_contract.py
index bda9742c1..e5c4a60c3 100644
--- a/contract_sale_generation/models/abstract_contract.py
+++ b/contract_sale_generation/models/abstract_contract.py
@@ -6,14 +6,12 @@ from odoo import fields, models
class ContractAbstractContract(models.AbstractModel):
- _inherit = 'contract.abstract.contract'
+ _inherit = "contract.abstract.contract"
type = fields.Selection(
- [('invoice', 'Invoice'),
- ('sale', 'Sale')],
- string='Type',
- default='invoice',
+ [("invoice", "Invoice"), ("sale", "Sale")],
+ string="Type",
+ default="invoice",
required=True,
)
- sale_autoconfirm = fields.Boolean(
- string='Sale Autoconfirm')
+ sale_autoconfirm = fields.Boolean(string="Sale Autoconfirm")
diff --git a/contract_sale_generation/models/contract.py b/contract_sale_generation/models/contract.py
index 17c622d12..d8d966e5d 100644
--- a/contract_sale_generation/models/contract.py
+++ b/contract_sale_generation/models/contract.py
@@ -8,24 +8,26 @@
# Copyright 2018 Therp BV .
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-from odoo import api, fields, models, _
+from odoo import _, api, fields, models
class ContractContract(models.Model):
- _inherit = 'contract.contract'
+ _inherit = "contract.contract"
sale_count = fields.Integer(compute="_compute_sale_count")
@api.multi
def _prepare_sale(self, date_ref):
self.ensure_one()
- sale = self.env['sale.order'].new({
- 'partner_id': self.partner_id,
- 'date_order': fields.Date.to_string(date_ref),
- 'origin': self.name,
- 'company_id': self.company_id.id,
- 'user_id': self.partner_id.user_id.id,
- })
+ sale = self.env["sale.order"].new(
+ {
+ "partner_id": self.partner_id,
+ "date_order": fields.Date.to_string(date_ref),
+ "origin": self.name,
+ "company_id": self.company_id.id,
+ "user_id": self.partner_id.user_id.id,
+ }
+ )
if self.payment_term_id:
sale.payment_term_id = self.payment_term_id.id
if self.fiscal_position_id:
@@ -37,10 +39,11 @@ class ContractContract(models.Model):
@api.multi
def _get_related_sales(self):
self.ensure_one()
- sales = (self.env['sale.order.line']
- .search([('contract_line_id', 'in',
- self.contract_line_ids.ids)
- ]).mapped('order_id'))
+ sales = (
+ self.env["sale.order.line"]
+ .search([("contract_line_id", "in", self.contract_line_ids.ids)])
+ .mapped("order_id")
+ )
return sales
@api.multi
@@ -51,20 +54,18 @@ class ContractContract(models.Model):
@api.multi
def action_show_sales(self):
self.ensure_one()
- tree_view = self.env.ref('sale.view_order_tree',
- raise_if_not_found=False)
- form_view = self.env.ref('sale.view_order_form',
- raise_if_not_found=False)
+ tree_view = self.env.ref("sale.view_order_tree", raise_if_not_found=False)
+ form_view = self.env.ref("sale.view_order_form", raise_if_not_found=False)
action = {
- 'type': 'ir.actions.act_window',
- 'name': 'Sales Orders',
- 'res_model': 'sale.order',
- 'view_type': 'form',
- 'view_mode': 'tree,kanban,form,calendar,pivot,graph,activity',
- 'domain': [('id', 'in', self._get_related_sales().ids)],
+ "type": "ir.actions.act_window",
+ "name": "Sales Orders",
+ "res_model": "sale.order",
+ "view_type": "form",
+ "view_mode": "tree,kanban,form,calendar,pivot,graph,activity",
+ "domain": [("id", "in", self._get_related_sales().ids)],
}
if tree_view and form_view:
- action['views'] = [(tree_view.id, 'tree'), (form_view.id, 'form')]
+ action["views"] = [(tree_view.id, "tree"), (form_view.id, "form")]
return action
@api.multi
@@ -77,10 +78,10 @@ class ContractContract(models.Model):
for sale_rec in sales:
self.message_post(
body=_(
- 'Contract manually sale order: '
+ "Contract manually sale order: "
''
- 'Sale Order'
- ''
+ "Sale Order"
+ ""
)
% (sale_rec._name, sale_rec.id)
)
@@ -107,14 +108,12 @@ class ContractContract(models.Model):
continue
sale_values = contract._prepare_sale(date_ref)
for line in contract_lines:
- sale_values.setdefault('order_line', [])
+ sale_values.setdefault("order_line", [])
invoice_line_values = line._prepare_sale_line(
sale_values=sale_values,
)
if invoice_line_values:
- sale_values['order_line'].append(
- (0, 0, invoice_line_values)
- )
+ sale_values["order_line"].append((0, 0, invoice_line_values))
sales_values.append(sale_values)
contract_lines._update_recurring_next_date()
return sales_values
@@ -123,7 +122,7 @@ class ContractContract(models.Model):
def _recurring_create_sale(self, date_ref=False):
sales_values = self._prepare_recurring_sales_values(date_ref)
so_rec = self.env["sale.order"].create(sales_values)
- for rec in self.filtered(lambda c: c.sale_autoconfirm):
+ for _rec in self.filtered(lambda c: c.sale_autoconfirm):
so_rec.action_confirm()
return so_rec
@@ -132,11 +131,10 @@ class ContractContract(models.Model):
if not date_ref:
date_ref = fields.Date.context_today(self)
domain = self._get_contracts_to_invoice_domain(date_ref)
- domain.extend([('type', '=', 'sale')])
+ domain.extend([("type", "=", "sale")])
sales = self.env["sale.order"]
# Sales by companies, so assignation emails get correct context
- companies_to_sale = self.read_group(
- domain, ["company_id"], ["company_id"])
+ companies_to_sale = self.read_group(domain, ["company_id"], ["company_id"])
for row in companies_to_sale:
contracts_to_sale = self.search(row["__domain"]).with_context(
allowed_company_ids=[row["company_id"][0]]
@@ -149,15 +147,13 @@ class ContractContract(models.Model):
if not date_ref:
date_ref = fields.Date.context_today(self)
domain = self._get_contracts_to_invoice_domain(date_ref)
- domain.extend([('type', '=', 'invoice')])
+ domain.extend([("type", "=", "invoice")])
invoices = self.env["account.invoice"]
# Invoice by companies, so assignation emails get correct context
- companies_to_invoice = self.read_group(
- domain, ["company_id"], ["company_id"])
+ companies_to_invoice = self.read_group(domain, ["company_id"], ["company_id"])
for row in companies_to_invoice:
contracts_to_invoice = self.search(row["__domain"]).with_context(
allowed_company_ids=[row["company_id"][0]]
)
- invoices |= contracts_to_invoice._recurring_create_invoice(
- date_ref)
+ invoices |= contracts_to_invoice._recurring_create_invoice(date_ref)
return invoices
diff --git a/contract_sale_generation/models/contract_line.py b/contract_sale_generation/models/contract_line.py
index bdb571fe0..bc9df45af 100644
--- a/contract_sale_generation/models/contract_line.py
+++ b/contract_sale_generation/models/contract_line.py
@@ -5,7 +5,7 @@ from odoo import api, models
class ContractLine(models.Model):
- _inherit = 'contract.line'
+ _inherit = "contract.line"
@api.multi
def _prepare_sale_line(self, order_id=False, sale_values=False):
@@ -14,22 +14,30 @@ class ContractLine(models.Model):
self.last_date_invoiced, self.recurring_next_date
)
sale_line_vals = {
- 'product_id': self.product_id.id,
- 'product_uom_qty': self._get_quantity_to_invoice(*dates),
- 'uom_id': self.uom_id.id,
- 'discount': self.discount,
- 'contract_line_id': self.id,
- 'display_type': self.display_type,
+ "product_id": self.product_id.id,
+ "product_uom_qty": self._get_quantity_to_invoice(*dates),
+ "uom_id": self.uom_id.id,
+ "discount": self.discount,
+ "contract_line_id": self.id,
+ "display_type": self.display_type,
}
if order_id:
- sale_line_vals['order_id'] = order_id.id
- order_line = self.env['sale.order.line'].with_context(
- force_company=self.contract_id.company_id.id,
- ).new(sale_line_vals)
- if sale_values and not order_id:
- sale = self.env['sale.order'].with_context(
+ sale_line_vals["order_id"] = order_id.id
+ order_line = (
+ self.env["sale.order.line"]
+ .with_context(
force_company=self.contract_id.company_id.id,
- ).new(sale_values)
+ )
+ .new(sale_line_vals)
+ )
+ if sale_values and not order_id:
+ sale = (
+ self.env["sale.order"]
+ .with_context(
+ force_company=self.contract_id.company_id.id,
+ )
+ .new(sale_values)
+ )
order_line.order_id = sale
# Get other order line values from product onchange
order_line.product_id_change()
@@ -38,10 +46,10 @@ class ContractLine(models.Model):
name = self._insert_markers(dates[0], dates[1])
sale_line_vals.update(
{
- 'sequence': self.sequence,
- 'name': name,
- 'analytic_tag_ids': [(6, 0, self.analytic_tag_ids.ids)],
- 'price_unit': self.price_unit,
+ "sequence": self.sequence,
+ "name": name,
+ "analytic_tag_ids": [(6, 0, self.analytic_tag_ids.ids)],
+ "price_unit": self.price_unit,
}
)
return sale_line_vals
diff --git a/contract_sale_generation/models/sale_order_line.py b/contract_sale_generation/models/sale_order_line.py
index 08abb1332..cb9451ce8 100644
--- a/contract_sale_generation/models/sale_order_line.py
+++ b/contract_sale_generation/models/sale_order_line.py
@@ -5,8 +5,8 @@ from odoo import fields, models
class SaleOrderLine(models.Model):
- _inherit = 'sale.order.line'
+ _inherit = "sale.order.line"
contract_line_id = fields.Many2one(
- 'contract.line', string='Contract Line', index=True
+ "contract.line", string="Contract Line", index=True
)
diff --git a/contract_sale_generation/readme/DESCRIPTION.rst b/contract_sale_generation/readme/DESCRIPTION.rst
index 2ea92d9eb..f8ff4d193 100644
--- a/contract_sale_generation/readme/DESCRIPTION.rst
+++ b/contract_sale_generation/readme/DESCRIPTION.rst
@@ -1,2 +1,2 @@
-This module extends functionality of contracts to be able to generate sales
+This module extends functionality of contracts to be able to generate sales
orders instead of invoices.
diff --git a/contract_sale_generation/tests/test_contract_sale.py b/contract_sale_generation/tests/test_contract_sale.py
index 1f83a4e93..14f8aa5ae 100644
--- a/contract_sale_generation/tests/test_contract_sale.py
+++ b/contract_sale_generation/tests/test_contract_sale.py
@@ -3,8 +3,8 @@
# Copyright 2017 Angel Moya
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-from odoo.exceptions import ValidationError
from odoo import fields
+from odoo.exceptions import ValidationError
from odoo.tests.common import TransactionCase
@@ -17,95 +17,95 @@ class TestContractSale(TransactionCase):
def setUp(self):
super(TestContractSale, self).setUp()
- self.pricelist = self.env['product.pricelist'].create({
- 'name': 'pricelist for contract test',
- })
- self.partner = self.env['res.partner'].create({
- 'name': 'partner test contract',
- 'property_product_pricelist': self.pricelist.id,
- })
- self.product_1 = self.env.ref('product.product_product_1')
- self.product_1.taxes_id += self.env['account.tax'].search(
- [('type_tax_use', '=', 'sale')], limit=1
+ self.pricelist = self.env["product.pricelist"].create(
+ {
+ "name": "pricelist for contract test",
+ }
)
- self.product_1.description_sale = 'Test description sale'
+ self.partner = self.env["res.partner"].create(
+ {
+ "name": "partner test contract",
+ "property_product_pricelist": self.pricelist.id,
+ }
+ )
+ self.product_1 = self.env.ref("product.product_product_1")
+ self.product_1.taxes_id += self.env["account.tax"].search(
+ [("type_tax_use", "=", "sale")], limit=1
+ )
+ self.product_1.description_sale = "Test description sale"
self.line_template_vals = {
- 'product_id': self.product_1.id,
- 'name': 'Test Contract Template',
- 'quantity': 1,
- 'uom_id': self.product_1.uom_id.id,
- 'price_unit': 100,
- 'discount': 50,
- 'recurring_rule_type': 'yearly',
- 'recurring_interval': 1,
+ "product_id": self.product_1.id,
+ "name": "Test Contract Template",
+ "quantity": 1,
+ "uom_id": self.product_1.uom_id.id,
+ "price_unit": 100,
+ "discount": 50,
+ "recurring_rule_type": "yearly",
+ "recurring_interval": 1,
}
self.template_vals = {
- 'name': 'Test Contract Template',
- 'contract_line_ids': [
+ "name": "Test Contract Template",
+ "contract_line_ids": [
(0, 0, self.line_template_vals),
],
}
- self.template = self.env['contract.template'].create(
- self.template_vals
- )
+ self.template = self.env["contract.template"].create(self.template_vals)
# For being sure of the applied price
- self.env['product.pricelist.item'].create(
+ self.env["product.pricelist.item"].create(
{
- 'pricelist_id': self.partner.property_product_pricelist.id,
- 'product_id': self.product_1.id,
- 'compute_price': 'formula',
- 'base': 'list_price',
+ "pricelist_id": self.partner.property_product_pricelist.id,
+ "product_id": self.product_1.id,
+ "compute_price": "formula",
+ "base": "list_price",
}
)
- self.contract = self.env['contract.contract'].create(
+ self.contract = self.env["contract.contract"].create(
{
- 'name': 'Test Contract',
- 'partner_id': self.partner.id,
- 'pricelist_id': self.partner.property_product_pricelist.id,
- 'type': 'sale',
- 'sale_autoconfirm': False
+ "name": "Test Contract",
+ "partner_id": self.partner.id,
+ "pricelist_id": self.partner.property_product_pricelist.id,
+ "type": "sale",
+ "sale_autoconfirm": False,
}
)
self.line_vals = {
- 'contract_id': self.contract.id,
- 'product_id': self.product_1.id,
- 'name': 'Services from #START# to #END#',
- 'quantity': 1,
- 'uom_id': self.product_1.uom_id.id,
- 'price_unit': 100,
- 'discount': 50,
- 'recurring_rule_type': 'monthly',
- 'recurring_interval': 1,
- 'date_start': '2020-01-01',
- 'recurring_next_date': '2020-01-15',
+ "contract_id": self.contract.id,
+ "product_id": self.product_1.id,
+ "name": "Services from #START# to #END#",
+ "quantity": 1,
+ "uom_id": self.product_1.uom_id.id,
+ "price_unit": 100,
+ "discount": 50,
+ "recurring_rule_type": "monthly",
+ "recurring_interval": 1,
+ "date_start": "2020-01-01",
+ "recurring_next_date": "2020-01-15",
}
self.contract.contract_template_id = self.template
self.contract._onchange_contract_template_id()
- self.contract_line = self.env['contract.line'].create(
- self.line_vals
- )
- self.contract2 = self.env['contract.contract'].create(
+ self.contract_line = self.env["contract.line"].create(self.line_vals)
+ self.contract2 = self.env["contract.contract"].create(
{
- 'name': 'Test Contract 2',
- 'type': 'sale',
- 'partner_id': self.partner.id,
- 'pricelist_id': self.partner.property_product_pricelist.id,
- 'contract_type': 'purchase',
- 'contract_line_ids': [
+ "name": "Test Contract 2",
+ "type": "sale",
+ "partner_id": self.partner.id,
+ "pricelist_id": self.partner.property_product_pricelist.id,
+ "contract_type": "purchase",
+ "contract_line_ids": [
(
0,
0,
{
- 'product_id': self.product_1.id,
- 'name': 'Services from #START# to #END#',
- 'quantity': 1,
- 'uom_id': self.product_1.uom_id.id,
- 'price_unit': 100,
- 'discount': 50,
- 'recurring_rule_type': 'monthly',
- 'recurring_interval': 1,
- 'date_start': '2018-02-15',
- 'recurring_next_date': '2018-02-22',
+ "product_id": self.product_1.id,
+ "name": "Services from #START# to #END#",
+ "quantity": 1,
+ "uom_id": self.product_1.uom_id.id,
+ "price_unit": 100,
+ "discount": 50,
+ "recurring_rule_type": "monthly",
+ "recurring_interval": 1,
+ "date_start": "2018-02-15",
+ "recurring_next_date": "2018-02-22",
},
)
],
@@ -114,40 +114,36 @@ class TestContractSale(TransactionCase):
def test_check_discount(self):
with self.assertRaises(ValidationError):
- self.contract_line.write({'discount': 120})
+ self.contract_line.write({"discount": 120})
def test_contract(self):
- recurring_next_date = to_date('2020-02-15')
+ recurring_next_date = to_date("2020-02-15")
self.assertAlmostEqual(self.contract_line.price_subtotal, 50.0)
res = self.contract_line._onchange_product_id()
- self.assertIn('uom_id', res['domain'])
+ self.assertIn("uom_id", res["domain"])
self.contract_line.price_unit = 100.0
self.contract.partner_id = self.partner.id
self.contract.recurring_create_sale()
self.sale_monthly = self.contract._get_related_sales()
self.assertTrue(self.sale_monthly)
- self.assertEqual(
- self.contract_line.recurring_next_date, recurring_next_date
- )
+ self.assertEqual(self.contract_line.recurring_next_date, recurring_next_date)
self.order_line = self.sale_monthly.order_line[0]
self.assertTrue(self.order_line.tax_id)
self.assertAlmostEqual(self.order_line.price_subtotal, 50.0)
self.assertEqual(self.contract.user_id, self.sale_monthly.user_id)
def test_contract_autoconfirm(self):
- recurring_next_date = to_date('2020-02-15')
+ recurring_next_date = to_date("2020-02-15")
self.contract.sale_autoconfirm = True
self.assertAlmostEqual(self.contract_line.price_subtotal, 50.0)
res = self.contract_line._onchange_product_id()
- self.assertIn('uom_id', res['domain'])
+ self.assertIn("uom_id", res["domain"])
self.contract_line.price_unit = 100.0
self.contract.partner_id = self.partner.id
self.contract.recurring_create_sale()
self.sale_monthly = self.contract._get_related_sales()
self.assertTrue(self.sale_monthly)
- self.assertEqual(
- self.contract_line.recurring_next_date, recurring_next_date
- )
+ self.assertEqual(self.contract_line.recurring_next_date, recurring_next_date)
self.order_line = self.sale_monthly.order_line[0]
self.assertTrue(self.order_line.tax_id)
self.assertAlmostEqual(self.order_line.price_subtotal, 50.0)
@@ -160,19 +156,24 @@ class TestContractSale(TransactionCase):
self.contract.contract_template_id = self.template
self.contract._onchange_contract_template_id()
res = {
- 'contract_line_ids':
- [(0, 0, {
- 'product_id': self.product_1.id,
- 'name': 'Test Contract Template',
- 'quantity': 1,
- 'uom_id': self.product_1.uom_id.id,
- 'price_unit': 100,
- 'discount': 50,
- 'recurring_rule_type': 'yearly',
- 'recurring_interval': 1,
- })]
+ "contract_line_ids": [
+ (
+ 0,
+ 0,
+ {
+ "product_id": self.product_1.id,
+ "name": "Test Contract Template",
+ "quantity": 1,
+ "uom_id": self.product_1.uom_id.id,
+ "price_unit": 100,
+ "discount": 50,
+ "recurring_rule_type": "yearly",
+ "recurring_interval": 1,
+ },
+ )
+ ]
}
- del self.template_vals['name']
+ del self.template_vals["name"]
self.assertDictEqual(res, self.template_vals)
def test_contract_count_sale(self):
@@ -183,27 +184,26 @@ class TestContractSale(TransactionCase):
self.assertEqual(self.contract.sale_count, 3)
def test_contract_count_sale_2(self):
- orders = self.env['sale.order']
+ orders = self.env["sale.order"]
orders |= self.contract.recurring_create_sale()
orders |= self.contract.recurring_create_sale()
orders |= self.contract.recurring_create_sale()
action = self.contract.action_show_sales()
- self.assertEqual(set(action['domain'][0][2]), set(orders.ids))
+ self.assertEqual(set(action["domain"][0][2]), set(orders.ids))
def test_cron_recurring_create_sale(self):
- self.contract_line.date_start = '2020-01-01'
- self.contract_line.recurring_invoicing_type = 'post-paid'
- self.contract_line.date_end = '2020-03-15'
+ self.contract_line.date_start = "2020-01-01"
+ self.contract_line.recurring_invoicing_type = "post-paid"
+ self.contract_line.date_end = "2020-03-15"
self.contract_line._onchange_date_start()
contracts = self.contract2
for _i in range(10):
- contracts |= self.contract.copy({'type': 'sale'})
- self.env['contract.contract'].cron_recurring_create_sale()
- order_lines = self.env['sale.order.line'].search(
- [('contract_line_id', 'in',
- contracts.mapped('contract_line_ids').ids)]
+ contracts |= self.contract.copy({"type": "sale"})
+ self.env["contract.contract"].cron_recurring_create_sale()
+ order_lines = self.env["sale.order.line"].search(
+ [("contract_line_id", "in", contracts.mapped("contract_line_ids").ids)]
)
self.assertEqual(
- len(contracts.mapped('contract_line_ids')),
+ len(contracts.mapped("contract_line_ids")),
len(order_lines),
)
diff --git a/contract_sale_generation/views/contract.xml b/contract_sale_generation/views/contract.xml
index 647a8906c..a22babd61 100644
--- a/contract_sale_generation/views/contract.xml
+++ b/contract_sale_generation/views/contract.xml
@@ -3,38 +3,58 @@
contract.contract.form.recurring.sale.form
contract.contract
-
+
-
-
-
+
+
+
-
- {'invisible': ['|', ('create_invoice_visibility', '=', False),('type','!=','invoice')]}
-
-
-
+
+ {'invisible': ['|', ('create_invoice_visibility', '=', False),('type','!=','invoice')]}
+
+
+
+
+
+ {'invisible': [('type','!=','invoice')]}
-
- {'invisible': [('type','!=','invoice')]}
-
-
+
diff --git a/contract_sale_generation/views/contract_template.xml b/contract_sale_generation/views/contract_template.xml
index 9a4707c5a..129da21c7 100644
--- a/contract_sale_generation/views/contract_template.xml
+++ b/contract_sale_generation/views/contract_template.xml
@@ -3,10 +3,10 @@
contract.template form view (in contract)
contract.template
-
+
-
+
diff --git a/setup/contract_sale_generation/odoo/addons/contract_sale_generation b/setup/contract_sale_generation/odoo/addons/contract_sale_generation
new file mode 120000
index 000000000..b26b97f4c
--- /dev/null
+++ b/setup/contract_sale_generation/odoo/addons/contract_sale_generation
@@ -0,0 +1 @@
+../../../../contract_sale_generation
\ No newline at end of file
diff --git a/setup/contract_sale_generation/setup.py b/setup/contract_sale_generation/setup.py
new file mode 100644
index 000000000..28c57bb64
--- /dev/null
+++ b/setup/contract_sale_generation/setup.py
@@ -0,0 +1,6 @@
+import setuptools
+
+setuptools.setup(
+ setup_requires=['setuptools-odoo'],
+ odoo_addon=True,
+)