diff --git a/website_multi_company_separate_orders/README.rst b/website_multi_company_separate_orders/README.rst new file mode 100644 index 000000000..1c5320e13 --- /dev/null +++ b/website_multi_company_separate_orders/README.rst @@ -0,0 +1,43 @@ +========================================== + Real Multi Website (eCommerce extension) +========================================== + +Multi Website support in eCommerce Customization: + +* adds field ``website_ids`` to payment.acquirer +* adds field ``website_ids`` to product.template +* adds field ``website_ids`` to product.public.category + +Credits +======= + +Contributors +------------ +* `Kolushov Alexandr `__ + +Sponsors +-------- +* `IT-Projects LLC `__ + +Maintainers +----------- +* `IT-Projects LLC `__ + + To get a guaranteed support you are kindly requested to purchase the module at `odoo apps store `__. + + Thank you for understanding! + + `IT-Projects Team `__ + +Further information +=================== + +Demo: http://runbot.it-projects.info/demo/website-addons/12.0 + +HTML Description: https://apps.odoo.com/apps/modules/12.0/website_multi_company_sale/ + +Usage instructions: ``_ + +Changelog: ``_ + +Tested on Odoo 12.0 f34d4d33a09d33a12e427c2490b6526546114486 diff --git a/website_multi_company_separate_orders/__init__.py b/website_multi_company_separate_orders/__init__.py new file mode 100644 index 000000000..132774b91 --- /dev/null +++ b/website_multi_company_separate_orders/__init__.py @@ -0,0 +1,4 @@ +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from . import models +from . import controllers diff --git a/website_multi_company_separate_orders/__manifest__.py b/website_multi_company_separate_orders/__manifest__.py new file mode 100644 index 000000000..7550f6441 --- /dev/null +++ b/website_multi_company_separate_orders/__manifest__.py @@ -0,0 +1,34 @@ +# Copyright 2019 Kolushov Alexandr +# License MIT (https://opensource.org/licenses/MIT). +{ + "name": """Real Multi Website (eCommerce extension Customization)""", + "summary": """Multi Website support in eCommerce Customization""", + "category": "eCommerce", + # "live_test_url": "http://apps.it-projects.info/shop/product/website-multi-company?version=12.0", + "images": ["images/website_multi_company_sale_main.png"], + "version": "12.0.1.5.1", + "application": False, + "author": "IT-Projects LLC, Kolushov Alexandr", + "support": "apps@it-projects.info", + "website": "https://it-projects.info/team/yelizariev", + "license": "LGPL-3", + "price": 9.00, + "currency": "EUR", + "depends": ["website_multi_company_sale", "website_sale", "portal"], + "external_dependencies": {"python": [], "bin": []}, + "data": [ + "views/product_public_category_views.xml", + "views/sale_views.xml", + "views/website_views.xml", + "views/templates.xml", + "security/website_multi_company_sale_security.xml", + ], + "qweb": [], + "demo": [], + "post_load": None, + "pre_init_hook": None, + "post_init_hook": None, + "uninstall_hook": None, + "auto_install": False, + "installable": True, +} diff --git a/website_multi_company_separate_orders/controllers/__init__.py b/website_multi_company_separate_orders/controllers/__init__.py new file mode 100644 index 000000000..9153c2c3a --- /dev/null +++ b/website_multi_company_separate_orders/controllers/__init__.py @@ -0,0 +1,3 @@ +# License MIT (https://opensource.org/licenses/MIT). + +from . import main diff --git a/website_multi_company_separate_orders/controllers/main.py b/website_multi_company_separate_orders/controllers/main.py new file mode 100644 index 000000000..dc233dc53 --- /dev/null +++ b/website_multi_company_separate_orders/controllers/main.py @@ -0,0 +1,166 @@ +# Copyright 2019 Kolushov Alexandr +# License MIT (https://opensource.org/licenses/MIT). + +import json +import logging + +from odoo import http +from odoo.http import request + +from odoo.addons.website_sale.controllers.main import WebsiteSale + +_logger = logging.getLogger(__name__) + + +class WebsiteSaleExtended(WebsiteSale): + def _get_search_domain(self, *args, **kwargs): + domain = super(WebsiteSaleExtended, self)._get_search_domain(*args, **kwargs) + company = request.website.company_id + if not company: + return domain + return ["|", ("company_id", "in", company.child_ids.ids)] + domain + + def _check_and_update_child_order( + self, sale_order, product_id, add_qty, set_qty, force=False, **kw + ): + product_company_id = product_id.company_id.id + order_company = sale_order.company_id + website_id = sale_order.website_id + if not website_id.split_orders or not ( + product_company_id + and product_company_id != order_company.id + and ( + product_company_id in website_id.split_orders_companies.ids + or force + or (not add_qty and set_qty == 0) + ) + ): + return False + sale_order_child = sale_order.order_child_ids.filtered( + lambda so: so.company_id.id == product_company_id + ) + if not sale_order_child: + pricelist_id = sale_order.pricelist_id.id + sale_order_child = sale_order.sudo().create( + { + "order_parent_id": sale_order.id, + "company_id": product_company_id, + "partner_id": sale_order.partner_id.id, + "partner_invoice_id": sale_order.partner_invoice_id.id, + "partner_shipping_id": sale_order.partner_shipping_id.id, + "pricelist_id": pricelist_id, + } + ) + sale_order.write({"order_child_ids": [(4, sale_order_child.id)]}) + _logger.info( + "website_sale created new child order for company: %s for order: %s", + product_company_id, + sale_order.id, + ) + + # if set_qty == 0 and not add_qty: + # deleted_order_lines = sale_order_child.order_line.filtered(lambda ol: ol.product_id == product_id) + # deleted_order_lines.unlink() + # return sale_order_child + + product_custom_attribute_values = None + if kw.get("product_custom_attribute_values"): + product_custom_attribute_values = json.loads( + kw.get("product_custom_attribute_values") + ) + no_variant_attribute_values = None + if kw.get("no_variant_attribute_values"): + no_variant_attribute_values = json.loads( + kw.get("no_variant_attribute_values") + ) + + sale_order_child._cart_update( + product_id=product_id.id, + add_qty=add_qty, + set_qty=set_qty, + product_custom_attribute_values=product_custom_attribute_values, + no_variant_attribute_values=no_variant_attribute_values, + ) + sale_order.order_line.filtered(lambda ol: ol.product_id == product_id).unlink() + return sale_order_child + + @http.route() + def cart_update(self, product_id, add_qty=1, set_qty=0, **kw): + result = super(WebsiteSaleExtended, self).cart_update( + product_id, add_qty, set_qty, **kw + ) + + if add_qty: + sale_order = request.website.sale_get_order() + prod_id = request.env["product.product"].browse(int(product_id)) + self._check_and_update_child_order( + sale_order, prod_id, add_qty, set_qty, **kw + ) + + return result + + @http.route() + def cart_update_json( + self, product_id, line_id=None, add_qty=None, set_qty=None, display=True + ): + result = super(WebsiteSaleExtended, self).cart_update_json( + product_id, line_id, add_qty, set_qty, display + ) + + if set_qty: + sale_order = request.website.sale_get_order() + prod_id = request.env["product.product"].browse(int(product_id)) + self._check_and_update_child_order(sale_order, prod_id, add_qty, set_qty) + + return result + + @http.route( + ["/shop/split_order_for_daughter_companies/"], + type="json", + auth="public", + methods=["POST"], + website=True, + csrf=False, + ) + def split_order_for_daughter_companies(self, company_id=False): + order = request.website.sale_get_order() + lines = order.order_line.filtered( + lambda ol: ol.product_id.company_id.id == company_id + ) + if not lines: + return False + result = [] + for line in lines: + prod = line.product_id + result.append( + self._check_and_update_child_order( + order, prod, line.product_uom_qty, False, "Force" + ) + ) + return all(result) + + +# class MultiCompanyPortal(CustomerPortal): +# def _prepare_portal_layout_values(self): +# res = super(MultiCompanyPortal, self)._prepare_portal_layout_values() +# user = request.env.user +# partner = user.partner_id +# company_id = request.env.user.company_id +# +# SaleOrder = request.env['sale.order'].sudo() +# quotation_count = SaleOrder.search_count([ +# ('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]), +# ('state', 'in', ['sent', 'cancel']), +# ('company_id', 'in', [company_id.id] + company_id.child_ids.ids) +# ]) +# order_count = SaleOrder.search_count([ +# ('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]), +# ('state', 'in', ['sale', 'done']), +# ('company_id', 'in', [company_id.id] + company_id.child_ids.ids) +# ]) +# res.update({ +# 'quotation_count': quotation_count, +# 'order_count': order_count, +# }) +# import wdb; wdb.set_trace() +# return res diff --git a/website_multi_company_separate_orders/doc/changelog.rst b/website_multi_company_separate_orders/doc/changelog.rst new file mode 100644 index 000000000..9ee2b48b8 --- /dev/null +++ b/website_multi_company_separate_orders/doc/changelog.rst @@ -0,0 +1,4 @@ +`1.0.0` +------- + +- Init version diff --git a/website_multi_company_separate_orders/doc/index.rst b/website_multi_company_separate_orders/doc/index.rst new file mode 100644 index 000000000..fdcd3f508 --- /dev/null +++ b/website_multi_company_separate_orders/doc/index.rst @@ -0,0 +1,8 @@ +========================================== + Real Multi Website (eCommerce extension) +========================================== + +Installation +============ + +* `Install `__ this module in a usual way diff --git a/website_multi_company_separate_orders/images/website_multi_company_sale_main.png b/website_multi_company_separate_orders/images/website_multi_company_sale_main.png new file mode 100644 index 000000000..2478e3c9a Binary files /dev/null and b/website_multi_company_separate_orders/images/website_multi_company_sale_main.png differ diff --git a/website_multi_company_separate_orders/models/__init__.py b/website_multi_company_separate_orders/models/__init__.py new file mode 100644 index 000000000..64f7a49cb --- /dev/null +++ b/website_multi_company_separate_orders/models/__init__.py @@ -0,0 +1,5 @@ +# License MIT (https://opensource.org/licenses/MIT). + +from . import product_public_category +from . import sale_order +from . import website diff --git a/website_multi_company_separate_orders/models/product_public_category.py b/website_multi_company_separate_orders/models/product_public_category.py new file mode 100644 index 000000000..2f5aff695 --- /dev/null +++ b/website_multi_company_separate_orders/models/product_public_category.py @@ -0,0 +1,14 @@ +# Copyright 2019 Kolushov Alexandr +# License MIT (https://opensource.org/licenses/MIT). + +from odoo import fields, models + + +class ProductPublicCategory(models.Model): + _inherit = "product.public.category" + + company_id = fields.Many2one( + "res.company", + "Company", + default=lambda self: self.env["res.company"]._company_default_get(), + ) diff --git a/website_multi_company_separate_orders/models/sale_order.py b/website_multi_company_separate_orders/models/sale_order.py new file mode 100644 index 000000000..15a513389 --- /dev/null +++ b/website_multi_company_separate_orders/models/sale_order.py @@ -0,0 +1,77 @@ +# Copyright 2019 Kolushov Alexandr +# License MIT (https://opensource.org/licenses/MIT). + +from odoo import api, fields, models + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + order_parent_id = fields.Many2one("sale.order", "Parent Order", readonly=True) + order_child_ids = fields.One2many( + "sale.order", "order_parent_id", "Child Orders", readonly=True + ) + + @api.multi + def action_done(self): + result = super(SaleOrder, self).action_done() + children = self.mapped("order_child_ids") + if children: + children.action_done() + return result + + @api.multi + def action_confirm(self): + result = super(SaleOrder, self).action_confirm() + children = self.mapped("order_child_ids") + if children: + children.action_confirm() + return result + + @api.multi + def action_cancel(self): + result = super(SaleOrder, self).action_cancel() + children = self.mapped("order_child_ids") + if children: + children.action_cancel() + return result + + @api.multi + def write(self, values): + result = super(SaleOrder, self).write(values) + if "partner_id" in values: + for record in self: + if record.order_child_ids: + record.order_child_ids.write( + { + "partner_id": values["partner_id"], + "partner_invoice_id": values["partner_id"], + "partner_shipping_id": values["partner_id"], + } + ) + return result + + +# class AccountInvoice(models.Model): +# _inherit = 'account.invoice' +# +# @api.multi +# def register_payment(self, payment_line, writeoff_acc_id=False, writeoff_journal_id=False): +# result = super(AccountInvoice, self).register_payment(payment_line, writeoff_acc_id, writeoff_journal_id) +# for record in self: +# if record.state != 'paid': +# return result +# sale_line_ids = record.invoice_line_ids[0].sale_line_ids +# if sale_line_ids: +# order = sale_line_ids[0].order_id.sudo() +# children = order.order_child_ids.filtered(lambda o: o.invoice_status not in ['cancel', 'invoiced']) +# if children: +# children.action_cancel() +# parent = order.order_parent_id +# if parent: +# product_ids = order.order_line.mapped(lambda ol: ol.product_id.id) +# order_line_ids = parent.order_line.filtered(lambda ol: ol.product_id.id in product_ids) +# order_line_ids.write({ +# 'product_uom_qty': 0, +# }) +# return result diff --git a/website_multi_company_separate_orders/models/website.py b/website_multi_company_separate_orders/models/website.py new file mode 100644 index 000000000..ec9a6dc71 --- /dev/null +++ b/website_multi_company_separate_orders/models/website.py @@ -0,0 +1,53 @@ +# Copyright 2019 Kolushov Alexandr +# License MIT (https://opensource.org/licenses/MIT). + +from odoo import api, fields, models + + +class Website(models.Model): + _inherit = "website" + + split_orders = fields.Boolean( + string="Automatic Order Splitting", help="Split orders for Daughter companies" + ) + split_orders_companies = fields.Many2many( + "res.company", + string="Order Duplicating Companies", + help="Daughter companies where order is being automatically duplicated to", + ) + + @api.model + def website_domain(self, website_id=False): + res = super(Website, self).website_domain(website_id) + website_id = website_id or self.get_current_website() + if ( + not website_id + or self.env.context + and self.env.context.get("website_id", False) + ): + return res + + if type(website_id) == int: + website_id = self.env["website"].browse(website_id) + website_company_parent_id = website_id.sudo().company_id.sudo().parent_id + if website_company_parent_id: + child_websites = ( + self.env["website"] + .search([("company_id", "=", website_company_parent_id.id)]) + .ids + ) + upd_tuple = res[0][2] + tuple(child_websites) + upd_res_0 = list(res[0]) + upd_res_0[2] = upd_tuple + res[0] = tuple(upd_res_0) + return res + + +# class Users(models.Model): +# _inherit = 'res.users' +# +# @classmethod +# def _login(cls, db, login, password): +# res = super(Users, cls)._login(db, login, password) +# import wdb; wdb.set_trace() +# return res diff --git a/website_multi_company_separate_orders/security/website_multi_company_sale_security.xml b/website_multi_company_separate_orders/security/website_multi_company_sale_security.xml new file mode 100644 index 000000000..d29fe0ab2 --- /dev/null +++ b/website_multi_company_separate_orders/security/website_multi_company_sale_security.xml @@ -0,0 +1,51 @@ + + + + + + Users with current company_id field specified have access only for products from specified company and child companies + + ['|', '|', ('company_id', '=', user.company_id.id), ('company_id', '=', False), ('company_id', 'in', user.company_id.child_ids.ids)] + + + + Users with backend_website_id field specified have access only for sale orders from specied website + + [('website_id', 'in', [user.backend_website_id.id, user.website_id.id, False])] + true + + + + Users with current company_id field specified have access only for sale orders from specified company and child companies + + [('company_id', 'in', [user.company_id.id, False] + user.company_id.child_ids.ids)] + + + Users with current company_id field specified have access only for productpublic categories from specified company and child companies + + ['|', '|', ('company_id', '=', user.company_id.id), ('company_id', '=', False), ('company_id', 'in', user.company_id.child_ids.ids)] + + diff --git a/website_multi_company_separate_orders/static/description/icon.png b/website_multi_company_separate_orders/static/description/icon.png new file mode 100644 index 000000000..213cccf54 Binary files /dev/null and b/website_multi_company_separate_orders/static/description/icon.png differ diff --git a/website_multi_company_separate_orders/static/src/css/website_cart.css b/website_multi_company_separate_orders/static/src/css/website_cart.css new file mode 100644 index 000000000..97b6d6989 --- /dev/null +++ b/website_multi_company_separate_orders/static/src/css/website_cart.css @@ -0,0 +1,9 @@ +.separate_order { + color: #00a09d; + font-size: 13px; +} + +.separate_order:not(.child_order) { + cursor: pointer; + text-decoration: underline; +} diff --git a/website_multi_company_separate_orders/static/src/js/website_cart.js b/website_multi_company_separate_orders/static/src/js/website_cart.js new file mode 100644 index 000000000..5178bd43c --- /dev/null +++ b/website_multi_company_separate_orders/static/src/js/website_cart.js @@ -0,0 +1,30 @@ +/* Copyright 2019 Kolushov Alexandr + License MIT (https://opensource.org/licenses/MIT). */ +odoo.define("website_multi_company_separate_orders", function(require) { + "use strict"; + + var session = require("web.session"); + var sAnimations = require("website.content.snippets.animation"); + + sAnimations.registry.WebsiteSale.include({ + read_events: _.extend(sAnimations.registry.WebsiteSale.prototype.read_events, { + "click td.td-product_name .separate_order:not(.child_order)": + "_duplicate_order_request", + }), + + _duplicate_order_request: function(event) { + var cid = event.target.attributes.pcid.value; + if (!cid) { + return; + } + session + .rpc("/shop/split_order_for_daughter_companies/" + cid, {}) + .then(function(result) { + if (result) { + // Alert('Order was duplicated'); + location.reload(); + } + }); + }, + }); +}); diff --git a/website_multi_company_separate_orders/views/product_public_category_views.xml b/website_multi_company_separate_orders/views/product_public_category_views.xml new file mode 100644 index 000000000..8975b8c5c --- /dev/null +++ b/website_multi_company_separate_orders/views/product_public_category_views.xml @@ -0,0 +1,31 @@ + + + + + product.public.category.form + product.public.category + + + + + + + + + + product.public.category.tree + product.public.category + + + + + + + + + diff --git a/website_multi_company_separate_orders/views/sale_views.xml b/website_multi_company_separate_orders/views/sale_views.xml new file mode 100644 index 000000000..1590149e9 --- /dev/null +++ b/website_multi_company_separate_orders/views/sale_views.xml @@ -0,0 +1,29 @@ + + + + + sale.order.form + sale.order + + + + + + + + + + sale.order.tree + sale.order + + + + + + + + diff --git a/website_multi_company_separate_orders/views/templates.xml b/website_multi_company_separate_orders/views/templates.xml new file mode 100644 index 000000000..df1079bd0 --- /dev/null +++ b/website_multi_company_separate_orders/views/templates.xml @@ -0,0 +1,165 @@ + + + + +