diff --git a/docker-compose.yml b/docker-compose.yml index 9f21eaf..c4989a4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -39,7 +39,7 @@ services: - ./docker/x86:/etc/odoo:ro - ./odoo/addons/splashsync:/mnt/extra-addons/splashsync:ro - ../Py-Core:/mnt/splashpy -# entrypoint: bash /etc/odoo/entrypoint.sh odoo -i "splashsync,product,stock,website_sale_comparison" +# entrypoint: bash /etc/odoo/entrypoint.sh odoo -i "splashsync,product,stock,website_sale_comparison,purchase" # entrypoint: bash /etc/odoo/entrypoint.sh odoo -i "splashsync" entrypoint: bash /etc/odoo/entrypoint.sh odoo hostname: latest.odoo.local diff --git a/odoo/addons/splashsync/helpers/objects/__init__.py b/odoo/addons/splashsync/helpers/objects/__init__.py index 67da461..0a7fe56 100755 --- a/odoo/addons/splashsync/helpers/objects/__init__.py +++ b/odoo/addons/splashsync/helpers/objects/__init__.py @@ -24,3 +24,4 @@ from .products.attributes import AttributesHelper, ValuesHelper, LinesHelper from .products.images import ProductImagesHelper +from .products.suppliers import SupplierHelper diff --git a/odoo/addons/splashsync/helpers/objects/products/suppliers.py b/odoo/addons/splashsync/helpers/objects/products/suppliers.py new file mode 100644 index 0000000..36ed8d7 --- /dev/null +++ b/odoo/addons/splashsync/helpers/objects/products/suppliers.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# +# This file is part of SplashSync Project. +# +# Copyright (C) 2015-2019 Splash Sync +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# For the full copyright and license information, please view the LICENSE +# file that was distributed with this source code. +# + +from odoo import http + + +class SupplierHelper: + """Collection of Static Functions to manage Odoo Product Supplier Info""" + + vendorDomain = "res.partner" + domain = "product.supplierinfo" + filter = [("supplier", "=", True), ("is_company", "=", True)] + + @staticmethod + def first(product): + """ + Get Product First Supplier Info + :param product: Product + :return: None, product.supplierinfo + """ + if len(product.seller_ids) < 1: + return None + return product.seller_ids[0] + + @staticmethod + def create(product, vendor_name, vendor_price): + """ + Create a Product Supplier Info Object + :param product: Product + :param vendor_name: str + :param vendor_price: float + :return: None, product.supplierinfo + """ + from odoo.addons.splashsync.helpers import M2OHelper + # ====================================================================# + # Validate Supplier Partner + vendor_id = M2OHelper.verify_name(vendor_name, "name", SupplierHelper.vendorDomain, SupplierHelper.filter) + if vendor_id is None or vendor_id <= 0: + return None + try: + # ====================================================================# + # Create Supplier Info + supplier = SupplierHelper.getModel().create({ + "name": vendor_id, + "product_id": product.id, + "min_qty": 1, + "price": vendor_price, + }) + # ====================================================================# + # Connect Supplier Info + product.seller_ids = [(6, 0, [supplier.id])] + + return supplier + except Exception: + return None + + # ====================================================================# + # Odoo ORM Access + # ====================================================================# + + @staticmethod + def getModel(): + """Get Product Supplier Infos Model Class""" + return http.request.env[SupplierHelper.domain].sudo() diff --git a/odoo/addons/splashsync/objects/product.py b/odoo/addons/splashsync/objects/product.py index 0040b3a..4195a5f 100755 --- a/odoo/addons/splashsync/objects/product.py +++ b/odoo/addons/splashsync/objects/product.py @@ -13,7 +13,8 @@ from . import OdooObject from splashpy import const -from .products import ProductsVariants, ProductsAttributes, ProductsPrices, ProductsImages, ProductsFeatures, ProductsRelations +from .products import ProductsVariants, ProductsAttributes, ProductsPrices, ProductsImages +from .products import ProductsFeatures, ProductsRelations, ProductsSupplier class Product( @@ -23,7 +24,8 @@ class Product( ProductsPrices, ProductsImages, ProductsFeatures, - ProductsRelations + ProductsRelations, + ProductsSupplier ): # ====================================================================# # Splash Object Definition @@ -56,6 +58,7 @@ def get_composite_fields(): "rating_last_image", "rating_last_feedback", "sale_line_warn", "message_unread_counter", "purchase_line_warn", "price", "lst_price", "list_price", "price_extra", "variant_price_extra", "standard_price", + "service_to_purchase" ] @staticmethod diff --git a/odoo/addons/splashsync/objects/products/__init__.py b/odoo/addons/splashsync/objects/products/__init__.py index db21978..04be141 100644 --- a/odoo/addons/splashsync/objects/products/__init__.py +++ b/odoo/addons/splashsync/objects/products/__init__.py @@ -18,4 +18,5 @@ from .prices import ProductsPrices from .images import ProductsImages from .relations import ProductsRelations +from .supplier import ProductsSupplier diff --git a/odoo/addons/splashsync/objects/products/relations.py b/odoo/addons/splashsync/objects/products/relations.py index 33473ac..b02fe42 100644 --- a/odoo/addons/splashsync/objects/products/relations.py +++ b/odoo/addons/splashsync/objects/products/relations.py @@ -46,7 +46,7 @@ def buildRelationFields(self): FieldFactory.create(const.__SPL_T_VARCHAR__, "public_categ_ids", "Categorie Id") FieldFactory.microData("http://schema.org/Product", "publicCategoryId") FieldFactory.isReadOnly() - FieldFactory.create(const.__SPL_T_INLINE__, "public_categ", "Public Categorie") + FieldFactory.create(const.__SPL_T_VARCHAR__, "public_categ", "Public Categorie") FieldFactory.microData("http://schema.org/Product", "publicCategory") FieldFactory.addChoices(M2OHelper.get_name_values("product.public.category")) FieldFactory.isNotTested() diff --git a/odoo/addons/splashsync/objects/products/supplier.py b/odoo/addons/splashsync/objects/products/supplier.py new file mode 100644 index 0000000..360cb53 --- /dev/null +++ b/odoo/addons/splashsync/objects/products/supplier.py @@ -0,0 +1,224 @@ +# -*- coding: utf-8 -*- +# +# This file is part of SplashSync Project. +# +# Copyright (C) 2015-2019 Splash Sync +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# For the full copyright and license information, please view the LICENSE +# file that was distributed with this source code. +# + +from odoo import http +from splashpy import const, Framework +from splashpy.componants import FieldFactory +from splashpy.helpers import PricesHelper +from odoo.addons.splashsync.helpers import M2OHelper, SettingsManager, TaxHelper, SupplierHelper + +class ProductsSupplier: + """ + Access to product First Supplier Fields + """ + # Static List of First Supplier Field Ids + supplierFields = [ + "supplier_name", "supplier_sku", "supplier_min_qty", + "supplier_price", "supplier_price_dbl", "supplier_currency" + ] + # Static Storage for New Supplier Price + supplierCreatePrice = None + + def buildSupplierFields(self): + # ==================================================================== # + # Safety Check + if "seller_ids" not in self.getModel().fields_get(): + return + # ====================================================================# + # First Supplier Name + FieldFactory.create(const.__SPL_T_VARCHAR__, "supplier_name", "Supplier Name") + FieldFactory.microData("http://schema.org/Product", "supplierName") + FieldFactory.addChoices(M2OHelper.get_name_values(SupplierHelper.vendorDomain, SupplierHelper.filter)) + FieldFactory.isNotTested() + # ====================================================================# + # First Supplier Price as Double + FieldFactory.create(const.__SPL_T_DOUBLE__, "supplier_price_dbl", "Supplier Price (Float)") + FieldFactory.microData("http://schema.org/Product", "supplierPriceDbl") + FieldFactory.association("supplier_name") + # ==================================================================== # + # First Supplier Price + FieldFactory.create(const.__SPL_T_PRICE__, "supplier_price", "Supplier Price") + FieldFactory.microData("http://schema.org/Product", "supplierPrice") + FieldFactory.isWriteOnly(Framework.isDebugMode()) + FieldFactory.isNotTested() + # ====================================================================# + # First Supplier SKU + FieldFactory.create(const.__SPL_T_VARCHAR__, "supplier_sku", "Supplier SKU") + FieldFactory.microData("http://schema.org/Product", "mpn") + FieldFactory.association("supplier_name", "supplier_price_dbl") + # ====================================================================# + # First Supplier MOQ + FieldFactory.create(const.__SPL_T_INT__, "supplier_min_qty", "Supplier MOQ") + FieldFactory.microData("http://schema.org/Product", "supplierMinQty") + FieldFactory.association("supplier_name", "supplier_price_dbl") + # ====================================================================# + # First Supplier Currency + FieldFactory.create(const.__SPL_T_CURRENCY__, "supplier_currency", "Supplier Currency") + FieldFactory.microData("http://schema.org/Product", "supplierCurrency") + FieldFactory.isNotTested() + + def getSupplierFields(self, index, field_id): + """ + Get Product First Supplier Fields + :param index: str + :param field_id: str + :return: None + """ + # ==================================================================== # + # Check field_id this First Supplier Field... + if not self.isSupplierField(field_id): + return + # ==================================================================== # + # Read First Supplier Value + self._out[field_id] = self.__get_supplier_values(field_id) + self._in.__delitem__(index) + + def setSupplierFields(self, field_id, field_data): + """ + Set Product First Supplier Fields + :param field_id: str + :param field_data: hash + :return: None + """ + # ==================================================================== # + # Check field_id this First Supplier Field... + if not self.isSupplierField(field_id): + return + # ====================================================================# + # Try to fetch Current First Supplier + supplier = SupplierHelper.first(self.object) + # ====================================================================# + # Try to Create if Valid Supplier Info Provided + if supplier is None and self.__has_supplier_info(): + supplier = SupplierHelper.create(self.object, self._in["supplier_name"], self.supplierCreatePrice) + # ====================================================================# + # Unable to Load/Create Supplier Info + if supplier is None: + self._in.__delitem__(field_id) + return + # ====================================================================# + # Update Supplier Info + return self.__set_supplier_values(field_id, field_data, supplier) + + def __get_supplier_values(self, value_id): + """ + Get List of Attributes Values for given Field + :param value_id: str + :return: dict + """ + # ====================================================================# + # Load First Product Supplier Info + supplier = SupplierHelper.first(self.object) + if supplier is None: + return None + # ====================================================================# + # Get Value + if value_id == "supplier_name": + return supplier.name.name + elif value_id == "supplier_sku": + return supplier.product_code + elif value_id == "supplier_min_qty": + return supplier.min_qty + elif value_id == "supplier_price_dbl": + return supplier.price + elif value_id == "supplier_price": + # ==================================================================== # + # Load Product Configuration + is_adv_taxes = SettingsManager.is_prd_adv_taxes() + return PricesHelper.encode( + float(supplier.price), + TaxHelper.get_tax_rate(self.object.taxes_id, 'purchase') if not is_adv_taxes else float(0), + None, + supplier.currency_id.name + ) + elif value_id == "supplier_currency": + return supplier.currency_id.name + + return None + + def __set_supplier_values(self, field_id, field_data, supplier): + """ + Set Product Supplier Fields + :param field_id: str + :param field_data: hash + :param supplier: product.supplierinfo + :return: None + """ + # ====================================================================# + # Set Value + self._in.__delitem__(field_id) + if field_id == "supplier_name": + # ====================================================================# + # Validate & Update Supplier Partner + new_currency = M2OHelper.verify_name(field_data, "name", SupplierHelper.vendorDomain, SupplierHelper.filter) + if new_currency is not None and new_currency > 0: + M2OHelper.set_name( + supplier, "name", field_data, + domain=SupplierHelper.vendorDomain, filters=SupplierHelper.filter + ) + elif field_id == "supplier_sku": + supplier.product_code = field_data + elif field_id == "supplier_min_qty": + supplier.min_qty = field_data + elif field_id == "supplier_price_dbl": + supplier.price = field_data + elif field_id == "supplier_price": + try: + supplier.price = float(PricesHelper.taxExcluded(field_data)) + except TypeError: + supplier.price = 0 + elif field_id == "supplier_currency": + # ====================================================================# + # Validate & Update Supplier Partner + new_currency = M2OHelper.verify_name(field_data, "name", "res.currency") + if new_currency is not None and new_currency > 0: + M2OHelper.set_name(supplier, "currency_id", field_data, domain="res.currency") + + def __has_supplier_info(self): + """ + Verify Product Supplier Info are Available on Input Fields + :return: bool + """ + # ====================================================================# + # Check Supplier SKU is there + if "supplier_name" not in self._in: + return False + if str(self._in["supplier_name"]).__len__() < 3: + return False + # ====================================================================# + # Check Supplier Price is there + if "supplier_price_dbl" not in self._in and "supplier_price" not in self._in: + return False + # ====================================================================# + # Detect Received Supplier Price + self.supplierCreatePrice = None + if "supplier_price_dbl" in self._in: + self.supplierCreatePrice = self._in["supplier_price_dbl"] + if "supplier_price" in self._in: + self.supplierCreatePrice = PricesHelper.taxExcluded(self._in["supplier_price"]) + # ====================================================================# + # Verify Supplier Price is Valid + if self.supplierCreatePrice is None: + return False + try: + if float(self.supplierCreatePrice) <= 0: + return False + except Exception: + return False + + return True + + @staticmethod + def isSupplierField(field_id): + return field_id in ProductsSupplier.supplierFields diff --git a/setup.py b/setup.py index b6fac3d..ddf83c8 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ setup( name='splashsync', - version="0.1.0", + version="0.1.1", packages=find_packages(), namespace_packages=['odoo.addons.splashsync'], install_requires=["splashpy"],