def verify_name(object_name, index, domain, filters=[]): """ Validate Id :param object_name: int,str Id to Verify :param index: str Property Name :param domain: str Target Objects Domain :param filters: list Additional Search Filters :return: None, int """ # No Domain or Filter => Skip if not isinstance(object_name, str) or not isinstance( index, str) or not isinstance(domain, str): return None # Execute Domain Search with Filter results = http.request.env[domain].search([(index, '=ilike', object_name)] + filters) # Results Found => Ok if len(results) > 0: # More than One Result Found => Ok but Warning if len(results) > 1: war = "More than One result by name search: " war += "'" + object_name + "' Name was found " + str( len(results)) + " times" war += " on table '" + domain + "'. First value was used." Framework.log().warn(war) # Return first result return results[0].id else: return None
def create(self): """ Create a New Order :return: Order Object """ # ====================================================================# # Order Fields Inputs self.order_inputs() # ====================================================================# # Init List of required Fields req_fields = self.collectRequiredFields() if req_fields is False: return False # ==================================================================== # # Pre-Setup Default Team Id req_fields = self.setup_default_team(req_fields) # ====================================================================# # Create a New Simple Order new_order = self.getModel().create(req_fields) # ====================================================================# # Safety Check - Error if new_order is None: Framework.log().error("Order creation failed") return False return new_order
def ping(self): """ Ping Splash Server, No Encryption, Just Say Hello!! :rtype: bool """ # Create Soap Client soap_client = self.__get_client() wsId, wsKey, wsHost = self.config().identifiers() # Execute Ping Request try: soap_response = soap_client.Ping(id=wsId, data="test") # Catch Potential Errors except SoapFault as fault: Framework.log().on_fault(fault) return False except Exception as exception: Framework.log().fromException(exception) return False # Decode Response ping_response = unpack(soap_response.children().children().children().__str__(), False) # Verify Response if ping_response is False: return False return ping_response["result"] == "1"
def load(self, object_id): """Load Odoo Object by Id""" try: # ====================================================================# # Order Fields Inputs self.order_inputs() # ====================================================================# # Load Product Variant model = self.getModel().browse([int(object_id)]) if len(model) != 1: return False # ====================================================================# # Load Product Template for template in model.product_tmpl_id: self.template = template break except Exception: from splashpy import Framework Framework.log().warn("Unable to Load Odoo Product " + str(object_id)) return False # self.debug(model, template) return model
def connect(self): """ Connect Splash Server, With Encryption, Just Say Hello!! :rtype: bool """ # Create Soap Client soap_client = self.__get_client() wsId, wsKey, wsHost = self.config().identifiers() # Execute Connect Request try: soap_response = soap_client.Connect(id=wsId, data=pack({"connect": True})) # Catch Potential Errors except SoapFault as fault: Framework.log().on_fault(fault) return False except Exception as exception: Framework.log().fromException(exception) return False # Decode Response connect_response = unpack(soap_response.children().children().children().__str__()) # Verify Response if connect_response is False: return False return connect_response["result"] == "1"
def setMultilang(iso_code): """ Configure Current Field with Multilangual Options :param iso_code: str :return: void """ from splashpy.core.framework import Framework # Safety Check ==> Verify Language ISO Code if not isinstance(iso_code, str) or iso_code.__len__() < 2: return Framework.log().error( "Default Language ISO Code is Invalid") # Safety Check ==> Verify Field Type is Allowed if not FieldFactory.new['type'] in FieldFactory.__MULTILANG_TYPES__: return Framework.log().error( "This field type is not Multi-lang: " + FieldFactory.new['type']) # Default Language ==> Only Setup Language Option FieldFactory.addOption("language", iso_code) # Other Language ==> Complete Field Setup if not iso_code == FieldFactory.dfLanguage: FieldFactory.identifier(FieldFactory.new['id'] + "_" + iso_code) if FieldFactory.new['itemtype']: FieldFactory.microData( FieldFactory.new['itemtype'] + "/" + iso_code, FieldFactory.new['itemprop'])
def touch(attribute, attr_value, is_wnva): """ Find or Create a Product Attributes Value by Code & Value :param attribute: str, product.attribute Attribute Code Name or Product Attribute :param attr_value: None, str Attribute Value :param is_wnva: bool No Variant Attribute? :return: None, product.attribute.value """ # ====================================================================# # Detect Empty Attribute Values if attr_value is None or len(str(attr_value)) < 1: return None # ====================================================================# # STR? Search or Create Attribute by Code if isinstance(attribute, str): attribute = AttributesHelper.touch(attribute, is_wnva) if attribute is None: Framework.log().error("An Error Occurred while Loading Attribute") return None # ====================================================================# # Search for Value in Attribute values = attribute.value_ids.filtered( lambda r: r.name.lower() == attr_value.lower()) if len(values) > 0: return values[0] # ====================================================================# # Crate New Value for Attribute return ValuesHelper.create(attribute, attr_value)
def validate(): """Validate Field Definition""" from splashpy.core.framework import Framework # Verify - Field Type is Not Empty. if not isinstance(FieldFactory.new['type'], str) or FieldFactory.new['type'].__len__() < 3: return Framework.log().error("Field type is not defined") # Verify - Field Id is Not Empty. if not isinstance(FieldFactory.new['id'], str) or FieldFactory.new['id'].__len__() < 2: return Framework.log().error("Field ID is not defined") # # Verify - Field Id No Spacial Chars. # if not isinstance(FieldFactory.new.id, str) or FieldFactory.new.id.__len__() < 2: # Framework.log().error("Field IS is not defined") # return False # Verify - Field Name is Not Empty. if not isinstance(FieldFactory.new['name'], str) or FieldFactory.new['name'].__len__() < 3: return Framework.log().error("Field name is not defined") # Verify - Field Desc is Not Empty. if not isinstance(FieldFactory.new['desc'], str) or FieldFactory.new['desc'].__len__() < 3: return Framework.log().error("Field Description is not defined") return True
def remove(invoice, payment): """ Remove a Payment fom an Invoice :param invoice: account.invoice :param payment: account.payment :rtype: bool """ try: # ==================================================================== # # Unit Tests - Ensure Invoice is Open (Default draft) if Framework.isDebugMode() and invoice.state == 'draft': invoice.action_invoice_open() invoice.refresh() # ====================================================================# # Unit Tests => Force Journal to Allow Update Posted if Framework.isDebugMode(): payment.journal_id.update_posted = True # ====================================================================# # Cancel Payment if payment.state == "posted": payment.cancel() # ====================================================================# # Remove Payment invoice.payment_ids = [(3, payment.id, 0)] return True except Exception as exception: Framework.log().fromException(exception, False) return False
def encodefullname(self): """ Encode First Name, Last Name & Legal Name from Buffer :return: str """ # ==================================================================== # # Get Data To Encode first = self.fullname_buffer["first"] last = self.fullname_buffer["last"] legal = self.fullname_buffer["legal"] # ==================================================================== # # Safety Warn if legal == "": legal = "Legal Name not Defined" Framework.log().warn("Legal Name not defined") # ==================================================================== # # Encode if (isinstance(first, str)) and (len(first) > 0): if (isinstance(last, str)) and (len(last) > 0): result = first + ", " + last + " - " + legal # first, last - legal else: result = first + " - " + legal # first - legal else: result = legal # legal return result
def commit(splash_object, action, object_ids): """ Execute Splash Commit for this Odoo Object :return: bool """ # ====================================================================# # Try to detect User Name try: from odoo.http import request user_name = request.env.user.name except Exception: user_name = "Unknown User" # ==================================================================== # # Send Commit Notification try: # ==================================================================== # # Check if Commits Are Allowed if SettingsManager.is_no_commits(): return True # ==================================================================== # # Execute Commits with Client return OdooClient.get_client().commit( str(splash_object.name), object_ids, action, user_name, "[" + str(action).capitalize() + "]" + str(splash_object.desc) + " modified on Odoo") except Exception as exception: splashLogger = Framework.log() if splashLogger: Framework.log().fromException(exception, False) Framework.log().to_logging().clear() return False
def get_main_currency_id(): try: company = http.request.env['res.company']._get_main_company().read( []) return company[0]["currency_id"][0] except Exception as exception: Framework.log().fromException(exception) return None
def add_invoice_line(invoice, line_data): """ Add a New Line to an Invoice :param invoice: account.invoice :param line_data: dict :return: account.invoice.line """ # ====================================================================# # Load Account Id from Configuration account_id = OrderLinesHelper.detect_sales_account_id(invoice) # ====================================================================# # Safety Check if account_id is None or int(account_id) <= 0: Framework.log().error( "Unable to detect Account Id, Add Invoice Line skipped.") Framework.log().error("Please check your configuration.") return None # ====================================================================# # Prepare Minimal Order Line Data req_fields = { "invoice_id": invoice.id, "account_id": account_id, "sequence": 10 + len(invoice.invoice_line_ids), } # ====================================================================# # Link to Product try: req_fields["product_id"] = int( ObjectsHelper.id(line_data["product_id"])) except: pass # ==================================================================== # # Description # Qty Invoiced for field_id in OrderLinesHelper.__generic_fields: try: req_fields[field_id] = line_data[field_id] except: pass # ====================================================================# # Unit Price try: req_fields["price_unit"] = PricesHelper.extract( line_data["price_unit"], "ht") except: pass # ====================================================================# # Create Order Line try: return http.request.env["account.invoice.line"].create(req_fields) except Exception as exception: Framework.log().error( "Unable to create Invoice Line, please check inputs.") Framework.log().fromException(exception, False) Framework.log().dump(req_fields, "New Invoice Line") return None
def setPricesFields(self, field_id, field_data): # Check if Price Field... if not self.isPricesFields(field_id): return # ==================================================================== # # Extract Price try: tax_excl = float(PricesHelper.taxExcluded(field_data)) except TypeError: tax_excl = 0 # ==================================================================== # # Directs Updates of Buy Price # Directs Updates of Base Price if field_id in ["standard_price", "list_price"]: self.setSimple(field_id, tax_excl) # ==================================================================== # # Updates of Final Sell Prices elif field_id == "lst_price": self._set_final_price(tax_excl) # ==================================================================== # # Updates of Variant Final Sell Prices elif field_id == "variant_price": self._set_variant_price(tax_excl) # ==================================================================== # # Load Product Configuration if SettingsManager.is_prd_adv_taxes(): return # ==================================================================== # # Update Product Sell Taxes if field_id in ["lst_price", "list_price"]: tax_rate = PricesHelper.taxPercent(field_data) if tax_rate is not None and tax_rate > 0: tax = TaxHelper.find_by_rate(tax_rate, 'sale') if tax is None: return Framework.log().error( "Unable to Identify Tax ID for Rate " + str(tax_rate)) else: self.object.taxes_id = [(6, 0, [tax.id])] else: self.object.taxes_id = [(6, 0, [])] # ==================================================================== # # Update Product Buy Taxes if field_id == "standard_price": tax_rate = PricesHelper.taxPercent(field_data) if tax_rate is not None and tax_rate > 0: tax = TaxHelper.find_by_rate(tax_rate, 'purchase') if tax is None: return Framework.log().error( "Unable to Identify Tax ID for Rate " + str(tax_rate)) else: self.object.supplier_taxes_id = [(6, 0, [tax.id])] else: self.object.supplier_taxes_id = [(6, 0, [])]
def setContactFields(self, field_id, field_data): # ==================================================================== # # Filter on Field Id if field_id not in ["street", "zip", "city"]: return # ==================================================================== # # Safety Check - Detect Contact Type & Not Parse Fields Street, Zip & City if PartnersHelper.is_contact(self.object): Framework.log().warn("This Address is a Contact Type, Writing " + field_id + " skipped.") self._in.__delitem__(field_id) return self.setSimple(field_id, field_data)
def create(self): """ Create a New Address :return: Address Object """ # ====================================================================# # Order Fields Inputs self.order_inputs() # ====================================================================# # Safety Check - First Name is Required if "first" not in self._in: Framework.log().error( "No Legal Name provided, Unable to create Address") return False # ====================================================================# # Load First Name Field in Name Field self._in["name"] = self._in["first"] # ====================================================================# # Safety Check - Address Type is Required (Auto-provide if needed) if "type" not in self._in: self._in["type"] = "other" # ====================================================================# # Init List of required Fields req_fields = self.collectRequiredCoreFields() # ====================================================================# # Delete Name Field self._in.__delitem__("name") # ====================================================================# # Safety Check if req_fields.__len__() < 1: return False # ==================================================================== # # Pre-Setup Default Team Id req_fields = self.setup_default_team(req_fields) # ====================================================================# # Create a New Simple Address new_address = self.getModel().create(req_fields) # ====================================================================# # Safety Check - Error if new_address is None: Framework.log().error("Address is None") return False # ====================================================================# # Initialize Address Fullname buffer self.object = new_address self.initfullname() return new_address
def set(model, field_name, iso_lang, value): """ Set Translation for a Model Field :param model: model :param field_name: str :param iso_lang: str :param value: str :return: void """ try: TransHelper.getModel()._set_ids( model.__class__.__name__ + "," + field_name, "model", iso_lang, [model.id], value) except Exception as exception: from splashpy import Framework Framework.log().fromException(exception)
def validate_payments_amounts(invoice, payments): """ Check Payment Amounts ensure Invoice Can Close Strategy: Allow 0.01 error per invoice line. :param invoice: account.invoice :param payments: dict :return: bool """ # ==================================================================== # # Check if Feature is Enabled from odoo.addons.splashsync.helpers import SettingsManager if not SettingsManager.is_sales_check_payments(): return True # ==================================================================== # # Sum Received Payments... payments_total = 0 for payment_data in payments: payments_total += float( payment_data["amount"]) if InvoicePaymentsHelper.validate( payment_data) else 0 # ====================================================================# # Compute Allowed Margin margin = InvoicePaymentsHelper.__get_payment_margin(invoice) # ====================================================================# # Compare Payment Amount vs Invoice Residual if abs(float(invoice.amount_total) - float(payments_total)) < margin: return True return Framework.log().error("Payments Validation fail: " + str(payments_total) + ", expected " + str(invoice.amount_total))
def pack(rawdata, secured=True): """Package Splash Data before Transmission""" # Complete Message with System Information rawdata['debug'] = int(Framework.isDebugMode()) rawdata['verbose'] = int(Framework.isDebugMode()) # Complete Message with Log rawdata['log'] = Framework.log().export() # Encode Message to Xml xmlString = XmlManager.to_xml(rawdata) # Verify message is a string if not isinstance(xmlString, str): logging.error("Unable to convert Data to Xml") return False # Encode using correct method if secured is True: rawMessage = Encryption.encrypt(xmlString) else: rawMessage = str(base64.b64encode(xmlString.encode()), "UTF-8") # Verify message is a string if not isinstance(rawMessage, str): logging.error("Unable to Encrypt Xml String") return False return rawMessage
def is_thirdparty(model): """ Detect if Object is a Thirdparty :param model: Object :return: bool """ # ==================================================================== # # Safety Check - Partner if not PartnersHelper.is_partner(model): return False # ==================================================================== # # Check condition - No Parent Id if len(model.parent_id) > 0: Framework.log().warn('This Object has a Parent') return False return True
def setDefaultLanguage(iso_code): """Select Default Language for Field List""" from splashpy.core.framework import Framework # Safety Check ==> Verify Language ISO Code if not isinstance(iso_code, str) or iso_code.__len__() < 2: return Framework.log().error( "Default Language ISO Code is Invalid") # Store Default Language ISO Code FieldFactory.dfLanguage = iso_code
def unpack(rawdata, secured=True): """Unpack Received Splash Data to Object""" # Decode using correct method if secured is True: xmlString = Encryption.decrypt(rawdata) else: xmlString = str(base64.b64decode(rawdata, None, True), 'UTF-8') # Verify received message is a string if not isinstance(xmlString, str): return False # Decode Message to Object xmlData = XmlManager.to_object(xmlString) # Import Logs to Logger if 'log' in xmlData: Framework.log().append(xmlData['log']) return xmlData
def update(self, needed): """Update Current Odoo Object""" if not needed: return self.getObjectIdentifier() try: self.object.flush() except Exception as exception: return Framework.log().fromException(exception) return self.getObjectIdentifier()
def set_values(invoice, payment, payment_data): """ Set values of Payments Line :param invoice: account.invoice :param payment: None|account.payment :param payment_data: dict :rtype: None|int """ # ====================================================================# # Check if Payment Data are Valid if not InvoicePaymentsHelper.validate(payment_data): Framework.log().warn("Payment Data are incomplete or invalid") return None # ====================================================================# # Check if Payment Data are Modified if payment is not None and InvoicePaymentsHelper.compare( invoice, payment, payment_data): Framework.log().warn("Payments are Similar >> Update Skipped") return payment.id # ====================================================================# # Check if Invoice is Open if invoice.state != 'open' and not Framework.isDebugMode(): Framework.log().error( "Payments cannot be processed because the invoice is not open!" ) return None # ====================================================================# # Recreate Payment # ====================================================================# try: # ====================================================================# # Remove Payment Item if payment is not None: if not InvoicePaymentsHelper.remove(invoice, payment): return None # DEBUG # else: # Framework.log().warn("Payments Deleted >> "+payment.name) # ====================================================================# # Add Payment Item payment = InvoicePaymentsHelper.add(invoice, payment_data) # DEBUG # if payment is not None: # Framework.log().warn("Payments Created >> "+payment_data["name"]) except Exception as ex: # ====================================================================# # Update Failed => Line may be protected Framework.log().error(ex) return None return payment.id if payment is not None else None
def create(self): """ Create a New ThirdParty :return: ThirdParty Object """ # ====================================================================# # Order Fields Inputs self.order_inputs() # ====================================================================# # Safety Check - Legal Name is Required if "legal" not in self._in: Framework.log().error("No Legal Name provided, Unable to create ThirdParty") return False # ====================================================================# # Load Legal Name Field in Name Field self._in["name"] = self._in["legal"] # ====================================================================# # Init List of required Fields req_fields = self.collectRequiredCoreFields() # ====================================================================# # Delete Name Field self._in.__delitem__("name") # ====================================================================# # Safety Check if req_fields.__len__() < 1: return False # ==================================================================== # # Pre-Setup Default Team Id req_fields = self.setup_default_team(req_fields) # ====================================================================# # Create a New Simple ThirdParty new_thirdparty = self.getModel().create(req_fields) # ====================================================================# # Safety Check - Error if new_thirdparty is None: Framework.log().error("ThirdParty is None") return False # ====================================================================# # Initialize ThirdParty Fullname buffer self.object = new_thirdparty self.initfullname() return new_thirdparty
def detect_by_sku(self): """Detect Existing Products ID by SKU""" from odoo.addons.splashsync.helpers import SettingsManager # ==================================================================== # # Check if Product has Advanced Feature Value if not SettingsManager.is_prd_sku_detection(): return False # ==================================================================== # # Search for Product with this SKU model = self.getModel().name_search(self._in['name']) if len(model) != 1: return False # ==================================================================== # # Add User Notification from splashpy import Framework Framework.log().warn("Sku Detection Worked: " + str(self._in['name']) + " => " + str(model[0][0])) return int(model[0][0])
def debug(self, product, template): """Debug for Product Attributes Configuration""" # Debug Product Variants infos = "<br />Product Variants: " + str( template.product_variant_ids.ids) # Debug Product Attributes infos += "<br />Product Values: " for prd_value in product.attribute_value_ids: infos += "<br /> - " + prd_value.attribute_id.name infos += " => " + "[" + str(prd_value.id) + "] " + prd_value.name # Debug Product Attributes Line # infos += "<br />Product Lines: " # for prd_line in product.attribute_line_ids: # infos += "<br /> - "+prd_line.attribute_id.name+" Values => " # for value in prd_line.value_ids: # infos += "["+str(value.id)+"] "+value.name # infos += "<br /> - "+prd_line.attribute_id.name+" Template Values => " # for value in prd_line.product_template_value_ids: # infos += "["+str(value.id)+"] "+value.name # # Debug Template Attributes Line # infos += "<br />Template Lines: " # for tmpl_line in template.attribute_line_ids: # infos += "<br /> - "+tmpl_line.attribute_id.name+" Values => " # for value in tmpl_line.value_ids: # infos += "["+str(value.id)+"] "+value.name # infos += "<br /> - "+tmpl_line.attribute_id.name+" Template Values => " # for value in tmpl_line.product_template_value_ids: # infos += "["+str(value.id)+"] "+value.name # Debug Product Attributes Line infos += "<br />Template Valid Attribute Lines: " for prd_line in product.valid_product_template_attribute_line_ids: infos += "<br /> - " + prd_line.attribute_id.name + " Values => " for value in prd_line.value_ids: infos += " [" + str(value.id) + "] " + value.name infos += "<br /> - " + prd_line.attribute_id.name + " Template Values => " for value in prd_line.product_template_value_ids: infos += " [" + str(value.id) + "] " + value.name from splashpy import Framework Framework.log().dump(infos, "Attributes Debug")
def collectRequiredFields(self): """ Collect All Required Fields from Inputs :return: False|hash """ from splashpy import Framework # ====================================================================# # Safety Check - All required fields are there if self.is_ready_for_create() is not True: return Framework.log().error(self.is_ready_for_create()) # ====================================================================# # Safety Check - Tracking Policy is Required if "picking_policy" not in self._in: self._in["picking_policy"] = "one" # ====================================================================# # Collect Basic Core Fields req_fields = self.collectRequiredCoreFields() self._in.__delitem__("picking_policy") if req_fields is False or req_fields.__len__() < 1: return False # ====================================================================# # Collect Order Core Fields for field_id in OrderCore.__core_fields_ids: # ====================================================================# # Setup Order Date if field_id == "date_order": req_fields[field_id] = self._in[field_id] continue # ====================================================================# # Setup Order Relations req_fields[field_id] = int(ObjectsHelper.id(self._in[field_id])) object_filters = PartnersHelper.thirdparty_filter( ) if field_id is "partner_id" else PartnersHelper.address_filter() if not M2OHelper.verify_id(req_fields[field_id], "res.partner", object_filters): return Framework.log().error( "Unable to Identify Pointed Object: " + str(self._in[field_id])) # ====================================================================# # Safety Check return req_fields
def get(model, field_name, iso_lang, default=""): """ Get Translation for a Model Field :param model: model :param field_name: str :param iso_lang: str :return: str """ try: translations = TransHelper.getModel()._get_ids( model.__class__.__name__ + "," + field_name, "model", iso_lang, [model.id]) if model.id in translations and isinstance(translations[model.id], str): return translations[model.id] except Exception as exception: from splashpy import Framework Framework.log().fromException(exception) return "Translation Error" return default
def publish(): from splashpy.core.framework import Framework # Commit Last Created if not already done if FieldFactory.new is not None: FieldFactory.commit() # Safety Check if not FieldFactory.fields.__len__(): return Framework.log().error("Fields List is Empty") buffer = copy.copy(FieldFactory.fields) FieldFactory.fields = None return buffer