diff --git a/ext/3rd-party-addons/mobikul/.gitignore b/ext/3rd-party-addons/mobikul/.gitignore new file mode 100755 index 00000000..7e99e367 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/.gitignore @@ -0,0 +1 @@ +*.pyc \ No newline at end of file diff --git a/ext/3rd-party-addons/mobikul/LICENSE b/ext/3rd-party-addons/mobikul/LICENSE new file mode 100755 index 00000000..b5340393 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/LICENSE @@ -0,0 +1,398 @@ +SOFTWARE LICENCE AGREEMENT +========================== + +This AGREEMENT is made effective on the date of the purchase of the software +between Webkul Software Pvt. Ltd.,Company incorporated under the Companies +Act, 1956 (hereinafter referred to as “Licensor"), and the purchaser of the +software/ product (hereinafter referred to as "Licensee"). + + +Preamble +-------- + +Licensor is a web and mobile product based organization engaged in the +business of developing and marketing software for enterprise level e-commerce +businesses. It is an ISO and NSR (NASSCOM) certified organization having a +team of more than 150 creative engineers which come from different +backgrounds. It has developed more than 700 web extensions and apps in the +past few years for open source platforms which are used and trusted globally. +Licensee now wishes to obtain license, and Licensor wishes to grant a license, +to allow use of the software so purchased in developing the e-commerce +business website/ mobile app of the Licensee, subject to the terms and +conditions set forth herein. + +THEREFORE, with the intent to be legally bound, the parties hereby agree as +follows: + + +Agreement +--------- + +1.DEFINITIONS. +As used in this Agreement, the following capitalized terms +shall have the definitions set forth below: + +"Derivative Works" are works developed by Licensee, its officers, agents, +contractors or employees, which are based upon, in whole or in part, the +Source Code and/or the Documentation and may also be based upon and/or +incorporate one or more other preexisting works of the Licensor. Derivative +Works may be any improvement, revision, modification, translation (including +compilation or recapitulation by computer), abridgment, condensation, +expansion, or any other form in which such a preexisting work may be recast, +transformed, or adapted. For purposes hereof, a Derivative Work shall also +include any compilation that incorporates such a preexisting work. + +"Documentation" is written, printed or otherwise recorded or stored (digital +or paper) material relating to the Software and/or Source Code, including +technical specifications and instructions for its use including Software/ +Source Code annotations and other descriptions of the principles of its +operation and instructions for its use. + +"Improvements" shall mean, with respect to the Software, all modifications and +changes made, developed, acquired or conceived after the date hereof and +during the entire term of this Agreement. + +"Source Code" is the computer programming source code form of the Software in +the form maintained by the Licensor, and includes all non-third-party +executables, libraries, components, and Documentation created or used in the +creation, development, maintenance, and support of the Software as well as all +updates, error corrections and revisions thereto provided by Licensor, in +whole or in part. + + +2.SOFTWARE LICENSE. + +(a)Grant of License. For the consideration set forth below, Licensor hereby +grants to Licensee, and Licensee hereby accepts the worldwide, non-exclusive, +perpetual, royalty-free rights and licenses set forth below: + +(i)The right and license to use and incorporate the software, in whole or in +part, to develop its website/ mobile app (including the integration of all or +part of the Licensor’s software into Licensee's own software) on one domain ( +Except Joomla modules , listed on store are entitled to be used on unlimited +domain as per the standard guidelines ) only, solely for the own personal or +business use of the Licensee. However, the License does not authorize the +Licensee to compile, copy or distribute the said Software or its Derivative +Works. + +(ii)The right and license does not authorize the Licensee to share any backup +or archival copies of the Software and / or the Source Code and Documentation +on any public internet space including github , stackoverflow etc . The +Licensee must ensure that the backup are not accessible to any other person +and the Licensee must prevent copying / use of source code by any unauthorized +persons. + +(iii)The right and license does not authorize the Licensee to migrate the +domain license to another domain. + +(iv)Our Joomla extensions are published under the GNU/GPL. + + +(b)Scope; Rights and Responsibilities. + +(i)Licensor shall enable the Licensee to download one complete copy of the +Software. + +(ii)The Software is intended for the sole use of the Licensee in development +of its own website/ mobile app. + +(iii)Licensee does not have the right to hand over, sell, distribute, +sub-license, rent, lease or lend any portion of the Software or Documentation, +whether modified or unmodified, to anyone. Licensee should not place the +Software on a server so that it becomes accessible via a public network such +as the Internet for distribution purposes. In case the Licensee is using any +source code management system like github, it can use the code there only when +it has paid subscription from such management system. + +(iv) In case the Licensee purchases the module and allow the third party +development agency to customize as per its need, it is at liberty to do so +subject to the condition that the Licensee as well as the Agency are not +authorized to sell the modified version of the extension. Except for the +required customization purposes, Licensee is not authorized to release the +Source Code, Derivative Work source code and/or Documentation to any third +party, which shall be considered as violation of the Agreement, inter-alia +entailing forthwith termination and legal action. + + +(c)Ownership. + +(i)Software and Source Code. All right, title, copyright, and interest in the +Software, Source Code, Software Modifications and Error corrections will be +and remain the property of Licensor. + +(ii)Derivative Works. As creation of Derivative Works by the Licensee is +prohibited, thus, all right, title, copyright, and interest in any and/or all +Derivative Works and Improvements created by, or on behalf of, Licensee will +also be deemed to the property of Licensor. Licensor shall be entitled to +protect copyright / intellectual property in all such Derivative Works and +Improvements also in any country as it may deem fit including without +limitation seeking copyright and/or patent protection. + + +3.CONSIDERATION. + +(a)Licensee shall pay to Licensor the amount as mentioned on the website from +where the order is placed, as one-time, upfront fees in consideration for the +licenses and rights granted hereunder (hereinafter referred to as the "License +Fee"). The License Fee to be paid by Licensee shall be paid upfront at the +time of placing the order, and no credit will be allowed under any +circumstances. + +(b)Once paid, the License Fees shall be non-refundable. The Licensee has fully +satisfied itself about the Software and has seen the demonstration, and only +thereafter has placed the order. Thus, the License Fees or any part thereof is +non-refundable. No claim for refund of the Licence Fees shall be entertained +under any circumstances. + + +4.REPRESENTATIONS AND WARRANTIES. + +(a)Mutual. Each of the parties represents and warrants to the other as +follows. + +(i)such party is a legal entity duly organized, validly existing and in good +standing; + +(ii)such party has the power and authority to conduct its business as +presently conducted and to enter into, execute, deliver and perform this +Agreement. + +(iii)This Agreement has been duly and validly accepted by such party and +constitutes the legal, valid and binding obligations of such party +respectively, enforceable against such party in accordance with their +respective terms; + +(iv)the acceptance, execution, delivery and performance of this Agreement does +not and will not violate such party's charter or by-laws; nor require any +consent, authorization, approval, exemption or other action by any third party +or governmental entity. + + +(b)Licensor warrants that, at the time of purchase of the Software: + +the Software will function materially as set forth in the website or published +functionality provided by Licensor to customers and potential customers +describing the Software; and + +Software add-ons, if purchased by the Licensee from the Licensor, will not +materially diminish the features or functions of or the specifications of the +Software as they existed as of the execution of this Agreement. + + +(c)Title. Licensor represents and warrants that it is the exclusive owner of +all copyright/ intellectual property in the Software (including the Source +Code) and has good and marketable title to the Software (including the Source +Code) free and clear of all liens, claims and encumbrances of any nature +whatsoever (collectively, "Liens"). Licensor's grant of license and rights to +Licensee hereunder does not, and will not infringe any third party's property, +intellectual property or personal rights. + + +5.TERM. + +(a)Subject to Licensee's payment obligations, this Agreement shall commence as +on the date of making payment of the Software by the Licensee to the Licensor, +and shall continue until terminated by either party. + +(b)The Licensor retains the right to terminate the license at any time, if the +Licensee is not abiding by any of the terms of the Agreement. The Licensee may +terminate the Agreement at any time at its own discretion by uninstalling the +Software and /or by destroying the said Software (or any copies thereof). +However, the Licensee shall not be entitled to seek any refund of the amount +paid by it to the Licensor, under any circumstances. + +(c)Survival. In the event this Agreement is terminated for any reason, the +provisions set forth in Sections 2(a), 2(b), and 2(c) shall survive. + + +6.INDEMNIFICATION. + +The Licensee release the Licensor from, and agree to indemnify, defend and +hold harmless the Licensor (and its officers, directors, employees, agents and +Affiliates) against, any claim, loss, damage, settlement, cost, taxes, expense +or other liability (including, without limitation, attorneys' fees) (each, a +"Claim") arising from or related to: (a) any actual or alleged breach of any +obligations in this Agreement; (b) any refund, adjustment, or return of +Software,(c) any claim for actual or alleged infringement of any Intellectual +Property Rights made by any third party or damages related thereto; or (d) +Taxes. + + +7.LIMITATION OF LIABILITY. + +The Licensor will not be liable for any direct, indirect, incidental, special, +consequential or exemplary damages, including but not limited to, damages for +loss of profits, goodwill, use, data or other intangible losses arising out of +or in connection with the Software, whether in contract, warranty, tort etc. ( +including negligence, software liability, any type of civil responsibility or +other theory or otherwise) to the Licensee or any other person for cost of +software, cover, recovery or recoupment of any investment made by the Licensee +or its affiliates in connection with this Agreement, or for any other loss of +profit, revenue, business, or data or punitive or consequential damages +arising out of or relating to this Agreement. Further, the aggregate liability +of the Licensor, arising out of or in connection with this Agreement or the +transactions contemplated hereby will not exceed at any time, or under any +circumstances, the total amounts received by the Licensor from the Licensee in +connection with the particular software giving rise to the claim. + + +8.FORCE MAJEURE. + +The Licensor will not be liable for any delay or failure to perform any of its +obligations under this Agreement by reasons, events or other matters beyond +its reasonable control. + + +9.RELATIONSHIP OF PARTIES. + +The Licensor and Licensee are independent legal entities, and nothing in this +Agreement will be construed to create a partnership, joint venture, +association of persons, agency, franchise, sales representative, or employment +relationship between the parties. The Licensee will have no authority to make +or accept any offers or representations on behalf of the Licensor. The +relationship between the parties is that of Licensor and Licensee only, and +the rights, duties, liabilities of each party shall be governed by this +Agreement. + + +10.MODIFICATION. + +The Licensor may amend any of the terms and conditions contained in this +Agreement at any time and solely at its discretion. Any changes will be +effective upon the posting of such changes on the Portal/ website, and the +Licensee is responsible for reviewing these changes and informing itself of +all applicable changes or notices. The continued use of a software by the +Licensee after posting of any changes by the Licensor, will constitute the +acceptance of such changes or modifications by the Licensee. + + +11.MISCELLANEOUS. + +(a)General Provisions. This Agreement: (i) may be amended only by a writing +signed by each of the parties; (ii) may be executed in several counterparts, +each of which shall be deemed an original but all of which shall constitute +one and the same instrument; (iii) contains the entire agreement of the +parties with respect to the transactions contemplated hereby and supersedes +all prior written and oral agreements, and all contemporaneous oral +agreements, relating to such transactions; (iv) shall be governed by, and +construed and enforced in accordance with, the laws of India; and (v) shall be +binding upon, and inure to the benefit of, the parties and their respective +successors and permitted assigns. Each of the parties hereby irrevocably +submits to the jurisdiction of the Courts at Delhi, India, for the purposes of +any action or proceeding arising out of or relating to this Agreement or the +subject matter hereof and brought by any other party. + +(b)Assignment. Except for the purpose of customization as mentioned in clause +2(b)(iv) above, Licensee cannot assign, pledge or otherwise transfer, whether +by operation of law or otherwise, this Agreement, or any of its obligations +hereunder, without the prior written consent of Licensor, which consent shall +not be unreasonably withheld. + +(c)Notices. Unless otherwise specifically provided herein, all notices, +consents, requests, demands and other communications required or permitted +hereunder: + +(i)shall be in writing; + +(ii)shall be sent by messenger, certified or registered mail/email, or +reliable express delivery service, to the appropriate address(es) set forth +below; and + +(iii)shall be deemed to have been given on the date of receipt by the +addressee, as evidenced by a receipt executed by the addressee (or a +responsible person in his or her office), the records of the Party delivering +such communication or a notice to the effect that such addressee refused to +claim or accept such communication, if sent by messenger, mail or express +delivery service. + +All such communications shall be sent to the following addresses or numbers, +or to such other addresses or numbers as any party may inform the others by +giving five days' prior notice: + +If to Webkul Software Pvt. Ltd.: + +Webkul Software Pvt. Ltd. +A-67, Sector 63, NOIDA – 201301, +Uttar Pradesh, India + +If to Licensee: +At the address mentioned by the Licensee +(at the time of placing order of generating Invoice) + +(d)Severability. It is the intent of the parties that the provisions of this +Agreement be enforced to the fullest extent permissible under the laws and +public policies of India in which enforcement hereof is sought. In +furtherance of the foregoing, each provision hereof shall be severable from +each other provision, and any provision hereof which is/ becomes unenforceable +shall be subject to the following: (i) if such provision is contrary to or +conflicts with any requirement of any statute, rule or regulation in effect, +then such requirement shall be incorporated into, or substituted for, such +unenforceable provision to the minimum extent necessary to make such provision +enforceable; (ii) the court, agency or arbitrator considering the matter is +hereby authorized to (or, if such court, agency or arbitrator is unwilling or +fails to do so, then the parties shall) amend such provision to the minimum +extent necessary to make such provision enforceable, and the parties hereby +consent to the entry of an order so amending such provision; and (iii) if +any such provision cannot be or is not reformed and made enforceable pursuant +to clause (i) or (ii) above, then such provision shall be ineffective to the +minimum extent necessary to make the remainder of this Agreement enforceable. +Any application of the foregoing provisions to any provision hereof shall not +effect the validity or enforceability of any other provision hereof. + +(e)By purchasing the Software, the Licensee acknowledge that it has read this +Agreement, and that it agrees to the content of the Agreement, its terms and +agree to use the Software in compliance with this Agreement. + +(f)The Licensor holds the sole copyright of the Software. The Software or any +portion thereof is a copyrightable matter and is liable to be protected by the +applicable laws. Copyright infringement in any manner can lead to prosecution +according to the current law. The Licensor reserves the right to revoke the +license of any user who is not holding any license or is holding an invalid +license. + +(g)This Agreement gives the right to use only one copy of the Software on one +domain solely for the own personal or business use of the Licensee, subject to +all the terms and conditions of this Agreement. A separate License has to be +purchased for each new Software installation. Any distribution of the Software +without the written consent of the Licensor (including non-commercial +distribution) is regarded as violation of this Agreement, and will entail +immediate termination of the Agreement and may invite liability, both civil +and criminal, as per applicable laws. + +(h)The Licensor reserves the rights to publish a selected list of users/ +Licensees of its Software, and no permission of any Licensee is needed in this +regard. The Licensee agrees that the Licensor may, in its sole discretion, +disclose or make available any information provided or submitted by the +Licensee or related to it under this Agreement to any judicial, +quasi-judicial, governmental, regulatory or any other authority as may be +required by the Licensor to co-operate and / or comply with any of their +orders, instructions or directions or to fulfill any requirements under +applicable Laws. + +(i)If the Licensee continues to use the Software even after the sending of the +notice by the Licensor for termination, the Licensee agree to accept an +injunction to restrain itself from its further use, and to pay all costs ( +including but not limited to reasonable attorney fees) to enforce injunction +or to revoke the License, and any damages suffered by the Licensor because of +the misuse of the Software by the Licensee. + + +12.ARBITRATION. + +If any dispute arises between the Licensor and the Licensee at any time, in +connection with the validity, interpretation, implementation or alleged breach +of any provision of this Agreement, the same shall be referred to a sole +Arbitrator who shall be an independent and neutral third party appointed +exclusively by the Licensor. The Licensee shall not object to the appointment +of the Arbitrator so appointed by the Licensor. The place of arbitration shall +be Delhi, India. The Arbitration & Conciliation Act, 1996 as amended by The +Arbitration & Conciliation (Amendment) Act, 2015, shall govern the +arbitration proceedings. The arbitration proceedings shall be held in the +English language. + + +This document is an electronic record in terms of Information Technology Act, +2000 and the amended provisions pertaining to electronic records in various +statutes as amended by the Information Technology Act, 2000. This electronic +record is generated by a computer system and does not require any physical or +digital signatures. \ No newline at end of file diff --git a/ext/3rd-party-addons/mobikul/__init__.py b/ext/3rd-party-addons/mobikul/__init__.py new file mode 100755 index 00000000..f3417c11 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/__init__.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +################################################################################# +# +# Copyright (c) 2015-Present Webkul Software Pvt. Ltd. () +# +################################################################################# +from . import models +from . import controllers + +def pre_init_check(cr): + from odoo.service import common + from odoo.exceptions import Warning + version_info = common.exp_version() + server_serie =version_info.get('server_serie') + if server_serie!='11.0':raise Warning('Module support Odoo series 11.0 found {}.'.format(server_serie)) + return True diff --git a/ext/3rd-party-addons/mobikul/__manifest__.py b/ext/3rd-party-addons/mobikul/__manifest__.py new file mode 100755 index 00000000..5c0c9f59 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/__manifest__.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +################################################################################# +# Author : Webkul Software Pvt. Ltd. () +# Copyright(c): 2015-Present Webkul Software Pvt. Ltd. +# All Rights Reserved. +# +# +# +# This program is copyright property of the author mentioned above. +# You can`t redistribute it and/or modify it. +# +# +# You should have received a copy of the License along with this program. +# If not, see +################################################################################# +{ + "name" : "Mobikul: Mobile App Builder", + "summary" : "This module allows you to convert your shop sales very easily with a native mobile application.", + "category" : "Sales", + "version" : "1.2.0", + "sequence" : 1, + "author" : "Webkul Software Pvt. Ltd.", + "license" : "Other proprietary", + "website" : "", + "description" : """""", + "live_test_url" : "http://demo.webkul.com/web/login", + "depends" : [ + 'website_sale'], + "data" : [ + 'security/mobikul_security.xml', + 'security/ir.model.access.csv', + 'data/mobikul_data.xml', + 'views/product_view.xml', + 'views/order_view.xml', + 'views/mobikul_views.xml', + 'views/res_config_view.xml', + 'views/menus.xml', + 'views/sync_cat_view.xml', + + ], + "demo" : ['data/demo_data_view.xml'], + "css" : [], + "js" : [], + "images" : ['static/description/Banner.png'], + "application" : True, + "installable" : True, + "auto_install" : False, + "price" : 599, + "currency" : "EUR", + "pre_init_hook" : "pre_init_check", +} diff --git a/ext/3rd-party-addons/mobikul/api.py b/ext/3rd-party-addons/mobikul/api.py new file mode 100755 index 00000000..248f5c46 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/api.py @@ -0,0 +1,211 @@ +apis={ + 'homepage':{ + 'description':'HomePage API', + 'uri':'/mobikul/homepage', + 'request':['POST'] + }, + + 'splashPageData':{ + 'description':'Default data to saved at app end.', + 'uri':'/mobikul/splashPageData', + 'request':['POST'] + }, + + 'sliderProducts':{ + 'description':'Product(s) of given Product Slider Record', + 'uri':'/mobikul/sliderProducts/;', + 'request':['POST'] + }, + + 'sliderProducts':{ + 'description':'Product(s) of given Product Slider Record', + 'uri':'/mobikul/search', + 'request':['POST'], + 'body':{ + "search":"l", + "offset":25 + } + }, + + 'MyOrders':{ + 'description':'', + 'uri':'/mobikul/my/orders', + 'request':['POST'], + + }, + + 'MyOrderDetail':{ + 'description':'', + 'uri':'/mobikul/my/order/', + 'request':['POST'], + }, + + + 'MyAddress':{ + 'description':'', + 'uri':'/mobikul/my/addresses', + 'request':['POST'], + }, + + 'setMyDefaultAddress':{ + 'description':'', + 'uri':'/mobikul/my/address/default/', + 'request':['PUT'], + }, + + 'addMyNewAddress':{ + 'description':'', + 'uri':'/mobikul/my/address/new', + 'request':['POST'], + 'body':{ + "name":"acdef", + "city":"xyz", + "zip":"0987456", + "street":"lmno", + "phone":"1236547890", + "county_id":"3", + "state_id" :"5" + } + }, + + 'editAddressDetail':{ + 'description':'', + 'uri':'/mobikul/my/address/', + 'request':['POST','PUT','DELETE'], + 'body':{ + "name":"acdef", + "city":"xyz", + "zip":"0987456", + "street":"lmno", + "phone":"1236547890", + "county_id":"3", + "state_id" :"5" + } + }, + + 'myAccount':{ + 'description':'', + 'uri':'/mobikul/my/account', + 'request':['POST'], + }, + + + 'localizationData':{ + 'description':'', + 'uri':'/mobikul/localizationData', + 'request':['POST'], + }, + + 'myCartDetails':{ + 'description':'', + 'uri':'/mobikul/mycart', + 'request':['POST'], + }, + + 'editCartDetail':{ + 'description':'', + 'uri':'/mobikul/mycart/', + 'request':['POST','PUT','DELETE'], + 'body':{ + 'set_qty':"3", + 'add_qty':"3" + } + }, + + 'emptyCart':{ + 'description':'', + 'uri':'/mobikul/mycart/setToEmpty', + 'request':['DELETE'], + 'body':{ + 'set_qty':"3", + 'add_qty':"3" + } + }, + + 'addToCart':{ + 'description':'', + 'uri':'/mobikul/mycart/addToCart', + 'request':['POST'], + 'body':{ + 'productId':"16", + 'set_qty':"3", + 'add_qty':"3" + } + }, + + 'paymentAcquirers':{ + 'description':'', + 'uri':'/mobikul/paymentAcquirers', + 'request':['POST'], + + }, + + 'orderReviewData':{ + 'description':'', + 'uri':'/mobikul/orderReviewData', + 'request':['POST'], + 'body':{ + "acquirerId":"1", + "shippingAddressId":"5" + } + }, + + 'placeMyOrders':{ + 'description':'', + 'uri':'/mobikul/placeMyOrder', + 'request':['POST'], + + }, + + + 'signUp':{ + 'description':'', + 'uri':'/mobikul/customer/signUp', + 'request':['POST'], + 'body':{ + "name":"Saurabh Gupta", + "login":"saurabh.gupta781@webkul.com", + "password":"123" + } + + }, + + 'login':{ + 'description':'', + 'uri':'/mobikul/customer/login', + 'request':['POST'], + }, + + 'signOut':{ + 'description':'', + 'uri':'/mobikul/customer/signOut', + 'request':['POST'], + }, + + + 'resetPassword':{ + 'description':'', + 'uri':'/mobikul/customer/resetPassword', + 'request':['POST'], + 'body':{ + "name":"Saurabh Gupta", + "login":"saurabh.gupta781@webkul.com", + "password":123 + } + }, + + 'signUp':{ + 'description':'', + 'uri':'/mobikul/customer/signUp', + 'request':['POST'], + 'body':{ + "name":"Saurabh Gupta", + "login":"saurabh.gupta781@webkul.com", + "password":123 + } + }, + + +} + + diff --git a/ext/3rd-party-addons/mobikul/changelog.md b/ext/3rd-party-addons/mobikul/changelog.md new file mode 100755 index 00000000..66d0f10c --- /dev/null +++ b/ext/3rd-party-addons/mobikul/changelog.md @@ -0,0 +1,41 @@ +module name : Mobikul: Mobile App Builder +technical name : mobikul + +## v[1.0.0] - 2017-10-26 +### Added +- +### Changed +- migrate version mobikul v1.0.5 from oddo v10.0 to odoo v11.0 +### Removed +- +### Fix +- + + + +## v[1.0.1] - 2017-11-21 +### Added +- Add email_verification api in mobikul app compatible to odoo v11.0 +- Add marketplace api in mobikul app compatible to odoo v11.0 +- Add wk_review api in mobikul app compatible to odoo v11.0 + +### Changed +- migrate version mobikul v1.0.0 to v1.0.1 +### Removed +- +### Fix +- + +## v[1.2.0] - 2018-3-28 +### Added +migrate changes that done in mobikul v 10.0 to mobikul 11.0 like: +-shipping methods +-payment methods +-some issues like translation etc. +### Changed +- migrate version mobikul v1.0.1 to v1.2.0 +### Removed +- +### Fix +- +[NOTE] supported wishlist module is website_sale_wishlist diff --git a/ext/3rd-party-addons/mobikul/controllers/__init__.py b/ext/3rd-party-addons/mobikul/controllers/__init__.py new file mode 100755 index 00000000..fb07eef4 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/controllers/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +################################################################################# +# +# Copyright (c) 2015-Present Webkul Software Pvt. Ltd. () +# +################################################################################# +from . import main \ No newline at end of file diff --git a/ext/3rd-party-addons/mobikul/controllers/main.py b/ext/3rd-party-addons/mobikul/controllers/main.py new file mode 100755 index 00000000..d6d86d55 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/controllers/main.py @@ -0,0 +1,1921 @@ +# -*- coding: utf-8 -*- +################################################################################# +# +# Copyright (c) 2015-Present Webkul Software Pvt. Ltd. () +# +################################################################################# +from odoo.addons.web.controllers.main import Home +import json +from urllib.parse import urlparse +import xml.etree.ElementTree as ET +import werkzeug +from odoo import _ +from odoo.http import request, Controller, route +import logging +_logger = logging.getLogger(__name__) +from base64 import b64decode +from ast import literal_eval +from functools import wraps +from odoo.addons.mobikul.models.mobikul import _displayWithCurrency, _get_image_url +import hashlib +import requests +from ast import literal_eval + +# from werkzeug.http import parse_authorization_header +# from odoo.http import Controller, dispatch_rpc +import re +TAG_RE = re.compile(r'<[^>]+>') + +def remove_htmltags(text): + return TAG_RE.sub('', text) + + +PRICE_FIELDS = [ +"amount_total","amount_untaxed","amount_tax", +"price_unit","price_subtotal","price_tax","price_total","price" +] + +AQUIRER_REF_CODES = [ +'COD','STRIPE_E','STRIPE_W', + "2_CHECKOUT","PAYFORT_SADAD","PAYFORT" +] +# For UI Controller payment Aquirer +# Note: Remenber in place order api (def placeorder) methods Acquire name for Ui controller gateway like '2_CHECKOUT' and 'PAYFORT_SADAD' should be same as in backend acquire name. + +PAYFORT_SDK_ENV_URL = { +"test":"https://sbpaymentservices.payfort.com/FortAPI/paymentApi", +"prod":"https://paymentservices.payfort.com/FortAPI/paymentApi" +} + +STATUS_MAPPING = { +"STRIPE": {'succeeded':'done','pending':'pending','failed':'error'}, +} + +import uuid +def _get_next_reference(order_name): + return order_name+"-"+str(uuid.uuid4())[:5] + + +class xml(object): + + @staticmethod + def _encode_content(data): + # .replace('&', '&') + return data.replace('<','<').replace('>','>').replace('"', '"') + + @classmethod + def dumps(cls, apiName, obj): + _logger.warning("%r : %r"%(apiName, obj)) + if isinstance(obj, dict): + return "".join("<%s>%s" % (key, cls.dumps(apiName, obj[key]), key) for key in obj) + elif isinstance(obj, list): + return "".join("<%s>%s" % ("I%s" % index, cls.dumps(apiName, element),"I%s" % index) for index,element in enumerate(obj)) + else: + return "%s" % (xml._encode_content(obj.__str__())) + + @staticmethod + def loads(string): + def _node_to_dict(node): + if node.text: + return node.text + else: + return {child.tag: _node_to_dict(child) for child in node} + root = ET.fromstring(string) + return {root.tag: _node_to_dict(root)} + +class WebServices(Controller): + + def __decorateMe(func): + @wraps(func) + def wrapped(inst, *args, **kwargs): + inst._mData = request.httprequest.data and json.loads(request.httprequest.data.decode('utf-8')) or {} + inst._mAuth = request.httprequest.authorization and (request.httprequest.authorization.get('password') or request.httprequest.authorization.get("username")) or None + inst.base_url = request.httprequest.host_url + inst._lcred = {} + inst._sLogin = False + inst.auth = True + inst._mLang = request.httprequest.headers.get("lang") or None + if request.httprequest.headers.get("Login"): + try: + inst._lcred = literal_eval(b64decode(request.httprequest.headers["Login"]).decode('utf-8')) + except: + inst._lcred = {"login":None,"pwd":None} + elif request.httprequest.headers.get("SocialLogin"): + inst._sLogin = True + try: + inst._lcred = literal_eval(b64decode(request.httprequest.headers["SocialLogin"]).decode('utf-8')) + except: + inst._lcred = {"authProvider":1,"authUserId":1234567890} + else: + inst.auth = False + return func(inst, *args, **kwargs) + return wrapped + + def _available_api(self): + API = { + 'homepage':{ + 'description':'HomePage API', + 'uri':'/mobikul/homepage' + }, + 'sliderProducts':{ + 'description':'Product(s) of given Product Slider Record', + 'uri':'/mobikul/sliderProducts/<int:product_slider_id>', + }, + 'login':{ + 'description':'Customer Login', + 'uri':'/mobikul/customer/login', + }, + 'signUp':{ + 'description':'Customer signUp', + 'uri':'/mobikul/customer/signUp', + }, + 'resetPassword':{ + 'description':'Customer Reset Password', + 'uri':'/mobikul/customer/resetPassword', + }, + 'splashPageData':{ + 'description':'Default data to saved at app end.', + 'uri':'/mobikul/splashPageData', + }, + } + return API + + def _wrap2xml(self, apiName, data): + resp_xml = "" + resp_xml += '' + resp_xml += "<%s>"%apiName + resp_xml += xml.dumps(apiName, data) + resp_xml += ""%apiName + resp_xml += '' + return resp_xml + + def _response(self, apiName, response, ctype='json'): + if 'local' in response: + response.pop("local") + if ctype=='json': + mime='application/json; charset=utf-8' + body = json.dumps(response) + else: + mime='text/xml' + body = self._wrap2xml(apiName,response) + headers = [ + ('Content-Type', mime), + ('Content-Length', len(body)) + ] + return werkzeug.wrappers.Response(body, headers=headers) + + @__decorateMe + def _authenticate(self, auth, **kwargs): + if 'api_key' in kwargs: + api_key = kwargs.get('api_key') + elif request.httprequest.authorization: + api_key = request.httprequest.authorization.get('password') or request.httprequest.authorization.get("username") + else: + api_key = False + + Mobikul = request.env['mobikul'].sudo() + response = Mobikul._validate(api_key,{"lang":self._mLang}) + if not response.get('success'): + return response + request.context = dict(request.context, pricelist=response.get('pricelist'), lang=response.get('lang'), base_url=self.base_url) + if auth: + Mobikul = request.env['mobikul'].sudo() + result = Mobikul.authenticate(self._lcred, kwargs.get('detailed',False),self._sLogin, context={'base_url':self.base_url}) + response.update(result) + return response + + @route('/mobikul/', csrf=False, type='http', auth="none") + def index(self, **kwargs): + """ HTTP METHOD : request.httprequest.method + """ + response = self._authenticate(False, **kwargs) + if response.get('success'): + data = self._available_api() + return self._response('mobikulApi', data, 'xml') + else: + headers=[('WWW-Authenticate','Basic realm="Welcome to Odoo Webservice, please enter the authentication key as the login. No password required."')] + return werkzeug.wrappers.Response('401 Unauthorized %r'%request.httprequest.authorization, status=401, headers=headers) + + @route('/mobikul/homepage', csrf=False, type='http', auth="none", methods=['POST']) + def getHomepage(self, **kwargs): + response = self._authenticate(False, **kwargs) + if response.get('success'): + response.update(self._languageData()) + Mobikul = request.env['mobikul'].sudo() + if self.auth: + result = Mobikul.authenticate(self._lcred, True, self._sLogin, context={'base_url':self.base_url}) + response.update(result) + local = response.get('local',{}) + context = {"base_url":self.base_url, "currencySymbol":local.get("currencySymbol",""), + "currencyPosition":local.get("currencyPosition",""),"lang_obj":local.get("lang_obj","")} + result = Mobikul.homePage(self._mData,context) + response.update(result) + self._tokenUpdate(customer_id=response.get('customerId')) + return self._response('homepage', response) + + @route(['/mobikul/sliderProducts/'], type='http', auth="none", csrf=False, methods=['GET','POST']) + def getSliderProducts(self, slider_id, **kwargs): + if request.httprequest.headers.get("Login"): + response = self._authenticate(True, **kwargs) + else: + response = self._authenticate(False, **kwargs) + if response.get('addons',{}).get('wishlist') and response.get('customerId'): + response['wishlist']= self._website_Wishlist(response.get('customerId')) + if response.get('success'): + PSlider = request.env['mobikul.product.slider'].sudo().search([('id','=',slider_id)]) + if PSlider: + local = response.get('local',{}) + context = { + 'limit':response.get('itemsPerPage',5), + 'offset':self._mData.get('offset',0), + 'order':self._mData.get('order',None), + "currencySymbol":local.get("currencySymbol",""), + "currencyPosition":local.get("currencyPosition",""), + 'lang_obj':local.get("lang_obj",""), + } + result = PSlider.get_product_data(context) + else: + result = {'success':False, 'message':'Product Slider not found !!!'} + response.update(result) + return self._response('sliderProducts', response) + + @route('/mobikul/customer/login', csrf=False, type='http', auth="none", methods=['POST']) + def login(self, **kwargs): + kwargs['detailed'] = True + response = self._authenticate(True, **kwargs) + self._tokenUpdate(customer_id=response.get('customerId')) + return self._response('login', response) + + @route('/mobikul/customer/signUp', csrf=False, type='http', auth="none", methods=['POST']) + def signUp(self, **kwargs): + response = self._authenticate(False, **kwargs) + if response.get('success'): + createNotification = False + Mobikul = request.env['mobikul'].sudo() + result = Mobikul.signUp(self._mData) + response.update(result) + if response['success'] and response.get('addons',{}).get('email_verification') and Mobikul.email_verification_defaults().get('send_email_on_signup'): + if not self._mData.get('authUserId',False): + response["message"] = _("An email has been sent to your email address. Please verify it.") + createNotification = True + if response['success']: + homepage = {} + login = {} + if self._mData.get('authUserId',False): + cred = {'authUserId':self._mData.get('authUserId',""),'authProvider':self._mData.get('authProvider',"")} + else: + cred = {'login':self._mData.get('login',""),'pwd':self._mData.get('password',"")} + login = Mobikul.authenticate(cred, True, self._sLogin, context={'base_url':self.base_url}) + response.update({"login":login,"cred":cred}) + local = response.get('local',{}) + context = { + "base_url":self.base_url, + "currencySymbol":local.get("currencySymbol",""), + "currencyPosition":local.get("currencyPosition",""), + "lang_obj":local.get('lang_obj'), + } + result = Mobikul.homePage(self._mData,context) + homepage.update(result) + response.update({"homepage":homepage}) + self._tokenUpdate(customer_id=response.get('customerId')) + if response.get("message","").startswith("Created"): + createNotification = True + if createNotification: + self._pushNotification( self._mData.get("fcmToken",""), customer_id = response.get('customerId') ) + return self._response('signUp', response) + + @route('/mobikul/customer/resetPassword', csrf=False, type='http', auth="none", methods=['POST']) + def resetPassword(self, **kwargs): + response = self._authenticate(False, **kwargs) + if response.get('success'): + Mobikul = request.env['mobikul'].sudo() + result = Mobikul.resetPassword(self._mData.get('login',False)) + response.update(result) + return self._response('resetPassword', response) + + @route('/mobikul/customer/signOut', csrf=False, type='http', auth="none", methods=['POST']) + def signOut(self, **kwargs): + response = self._authenticate(False, **kwargs) + if response.get('success'): + response['message'] = "Have a Good Day !!!" + self._tokenUpdate() + return self._response('signOut', response) + + @route('/mobikul/splashPageData', csrf=False, type='http', auth="none", methods=['POST']) + def getSplashPageData(self, **kwargs): + response = self._authenticate(False, **kwargs) + if response.get('success'): + Mobikul = request.env['mobikul'].sudo() + if 'login' in self._lcred: + result = Mobikul.authenticate(self._lcred, True,self._sLogin, context={'base_url':self.base_url}) + response.update(result) + result = Mobikul.getDefaultData() + response.update(result) + response['sortData'] = [ + ("Price: High to Low", "price desc"), + ("Price: Low to High", "price asc"), + ("Discounts", "id asc"), + ("Popularity", "id asc"), + ("Newest First", "id desc"), + ] + response.update(self._languageData()) + if response.get('addons',{}).get('review'): + response['RatingStatus'] = [ + ("1",_("Poor")), + ("2",_("Ok")), + ("3",_("Good")), + ("4",_("Very Good")), + ("5",_("Excellent")), + ] + return self._response('splashPageData', response) + + def _languageData(self): + mobikul = request.env['mobikul'].sudo().search([], limit=1) + temp = { + 'defaultLanguage':(mobikul.default_lang.code,mobikul.default_lang.name), + 'allLanguages':[(id.code,id.name) for id in mobikul.language_ids ] + } + + return temp + + @route('/mobikul/my/orders', csrf=False, type='http', auth="none", methods=['POST']) + def getMyOrders(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + PartnerObj = request.env['res.partner'].sudo() + Partner = PartnerObj.browse(response.get('customerId')) + if Partner: + result = {} + local = response.get('local',{}) + fields = ['name', 'create_date', 'state', 'amount_total', 'partner_shipping_id'] + SaleOrder = request.env['sale.order'].sudo() + + domain = [ + ('message_partner_ids', 'child_of', [Partner.commercial_partner_id.id]), + ('state','not in',('draft','sent')) + ] + if self._mData.get('date_from',False) and self._mData.get('date_to',False): + # domain += [('create_date', '>', context['date_from']), ('create_date', '<=', context['date_to'])] + domain += [('create_date', '>',self._mData.get('date_from') ), ('create_date', '<=', self._mData.get('date_to'))] + + result['tcount'] = SaleOrder.search_count(domain) + orders = SaleOrder.search_read(domain, limit=self._mData.get('limit',response.get('itemsPerPage',5)), offset=self._mData.get('offset',0), order="id desc", fields=fields) + result['recentOrders'] = [] + for order in orders: + ShippingAdd = PartnerObj.search([('id','=',order['partner_shipping_id'][0])]) + temp = { + 'id':order['id'], + 'name':order['name'] or "", + 'create_date':order['create_date'], + 'shipping_address':ShippingAdd and ShippingAdd._display_address() or "", + 'shipAdd_url':ShippingAdd and '/mobikul/my/address/%s'%ShippingAdd.id or "", + 'amount_total':_displayWithCurrency(local.get('lang_obj'),order['amount_total'], local.get('currencySymbol',""), local.get('currencyPosition',"")), + 'status':order['state'], + 'canReorder':True, + 'url':"/mobikul/my/order/%s"%order['id'], + } + result['recentOrders'].append(temp) + else: + result = {'success':False, 'message':'Customer not found !!!'} + response.update(result) + return self._response('orders', response) + + @route('/mobikul/my/order/', csrf=False, type='http', auth="none", methods=['POST']) + def getMyOrder(self, order_id, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + Order = request.env['sale.order'].sudo().search([('id','=',order_id)]) + if Order: + local = response.get('local',{}) + result = { + 'name':Order.name or "", + 'create_date':Order.create_date or "", + 'amount_total':_displayWithCurrency(local.get('lang_obj'),Order.amount_total, local.get('currencySymbol',""), local.get('currencyPosition',"")), + 'status':Order.state, + 'amount_untaxed':_displayWithCurrency(local.get('lang_obj'),Order.amount_untaxed, local.get('currencySymbol',""), local.get('currencyPosition',"")), + 'amount_tax':_displayWithCurrency(local.get('lang_obj'),Order.amount_tax, local.get('currencySymbol',""), local.get('currencyPosition',"")), + 'shipping_address':Order.partner_shipping_id._display_address(), + 'shipAdd_url':'/mobikul/my/address/%s'%Order.partner_shipping_id.id, + 'billing_address':Order.partner_invoice_id._display_address(), + } + result['items'] = [] + for line in Order.order_line: + if response.get('addons', {}).get('website_sale_delivery') and line.is_delivery: + shippingMethod = { + "tax":[tax.name for tax in line.tax_id], + "name":line.order_id.carrier_id.name, + "description":line.order_id.carrier_id.website_description or "", + "shippingId":line.order_id.carrier_id.id, + "total": _displayWithCurrency(local.get('lang_obj'), line.price_subtotal, + local.get('currencySymbol'), local.get('currencyPosition')), + } + result.update({"delivery":shippingMethod}) + else: + temp = { + 'name':line.name or "", + 'product_name':line.product_id and line.product_id.display_name or "", + 'qty':"%s %s"%(line.product_uom_qty, line.product_uom.name), + 'price_unit':_displayWithCurrency(local.get('lang_obj'),line.price_unit, local.get('currencySymbol',""), local.get('currencyPosition',"")), + 'price_subtotal':_displayWithCurrency(local.get('lang_obj'),line.price_subtotal, local.get('currencySymbol',""), local.get('currencyPosition',"")), + 'price_tax':_displayWithCurrency(local.get('lang_obj'),line.price_tax, local.get('currencySymbol',""), local.get('currencyPosition',"")), + 'price_total':_displayWithCurrency(local.get('lang_obj'),line.price_total, local.get('currencySymbol',""), local.get('currencyPosition',"")), + 'discount':"%s"%(line.discount and "%s %%"%line.discount or ""), + 'state':line.state, + 'thumbNail' :_get_image_url(self.base_url, 'product.product', line.product_id and line.product_id.id or "",'image'), + "templateId":line.product_id and line.product_id.product_tmpl_id.id or "", + } + result['items'].append(temp) + else: + result = {'success':False, 'message':'Order not found !!!'} + response.update(result) + return self._response('orders', response) + + @route('/mobikul/my/addresses', csrf=False, type='http', auth="none", methods=['POST']) + def getMyAddresses(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + PartnerObj = request.env['res.partner'].sudo() + Partner = PartnerObj.browse(response.get('customerId')) + if Partner: + result = {} + domain = [ + ('id', 'child_of', Partner.commercial_partner_id.ids), + ('id', 'not in', [Partner.id]), + ] + result['tcount'] = PartnerObj.search_count(domain) + 1 + addresses = PartnerObj.search(domain, limit=self._mData.get('limit',response.get('itemsPerPage',5)), offset=self._mData.get('offset',0), order="id desc") + result['addresses'] = [ + { + 'name':Partner.name, + 'display_name':Partner._display_address(), + 'url':"/mobikul/my/address/%s"%Partner.id, + 'addressId':Partner.id, + } + ] + # in result['addresses'][0] zero index address is billing address other is shipping address + for address in addresses: + temp = { + 'name':address.name, + 'display_name':address._display_address(), + 'url':"/mobikul/my/address/%s"%address.id, + 'addressId':address.id, + } + result['addresses'].append(temp) + else: + result = {'success':False, 'message':'Customer not found !!!'} + response.update(result) + return self._response('orders', response) + + @route('/mobikul/my/address/default/', csrf=False, type='http', auth="none", methods=['PUT']) + def setDefaultAddress(self, address_id, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + PartnerObj = request.env['res.partner'].sudo() + Address = PartnerObj.search([('id','=',address_id)]) + if Address: + result = {'message':'Updated successfully.'} + else: + result = {'success':False, 'message':'Address not found !!!'} + response.update(result) + return self._response('address', response) + + @route('/mobikul/my/address/new', csrf=False, type='http', auth="none", methods=['POST']) + def addMyAddress(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + PartnerObj = request.env['res.partner'].sudo() + vals = { + "name":self._mData.get('name',""), + "street":self._mData.get('street',""), + # "street2":self._mData.get('street2',""), + "zip":self._mData.get('zip',""), + "city":self._mData.get('city',""), + "phone":self._mData.get('phone',""), + "customer":1, + "type":"delivery", + "commercial_partner_id":int(response.get('customerId')), + "parent_id":int(response.get('customerId')), + } + try: + if self._mData.get("state_id"): + if request.env['res.country.state'].sudo().browse(int(self._mData["state_id"])).exists(): + vals["state_id"] = int(self._mData["state_id"]) + if self._mData.get("country_id"): + if request.env['res.country'].sudo().browse(int(self._mData["country_id"])).exists(): + vals["country_id"] = int(self._mData["country_id"]) + PartnerObj.create(vals) + result = {'message':'Created successfully.'} + except Exception as e: + result = {'success':False, 'message':'Error: Invalid Data'} + response.update(result) + return self._response('address', response) + + @route('/mobikul/my/address/', csrf=False, type='http', auth="none", methods=['POST','PUT','DELETE']) + def getMyAddress(self, address_id, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + PartnerObj = request.env['res.partner'].sudo() + Address = PartnerObj.search([('id','=',address_id)]) + if Address: + if request.httprequest.method in ["POST"]: + result = { + 'name':Address.name or "" , + 'street':Address.street or "", + # 'street2':Address.street2 or "", + 'zip':Address.zip or "", + 'city':Address.city or "", + 'state_id':Address.state_id and Address.state_id.id or "", + 'country_id':Address.country_id and Address.country_id.id or "", + 'phone':Address.phone or "", + # 'fax':Address.fax, + # 'mobile':Address.mobile, + } + elif request.httprequest.method == "PUT": + Address.name = self._mData.get('name',Address.name) + Address.street = self._mData.get('street',Address.street) + # Address.street2 = self._mData.get('street2',Address.street2) + Address.zip = self._mData.get('zip',Address.zip) + Address.city = self._mData.get('city',Address.city) + Address.phone = self._mData.get('phone',Address.phone) + try: + if self._mData.get("state_id"): + if request.env['res.country.state'].sudo().browse(int(self._mData["state_id"])).exists(): + Address.state_id = int(self._mData["state_id"]) + if self._mData.get("country_id"): + if request.env['res.country'].sudo().browse(int(self._mData["country_id"])).exists(): + Address.country_id = int(self._mData["country_id"]) + result = {'message':'Updated successfully.'} + except Exception as e: + result = {'success':False, 'message':'Error: Invalid Data'} + elif request.httprequest.method == "DELETE": + if response.get('customerId') != address_id: + Address.active = False + result = {'message':'Deleted successfully.'} + else: + result = {'success':False, 'message':_('Error: You can`t delete Billing Address.')} + else: + result = {'success':False, 'message':'Address not found !!!'} + response.update(result) + return self._response('address', response) + + @route('/mobikul/my/account', csrf=False, type='http', auth="none", methods=['POST']) + def getMyAccount(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + PartnerObj = request.env['res.partner'].sudo() + Partner = PartnerObj.browse(response.get('customerId')) + if Partner: + result = {} + result['data'] = { + 'name': {'required':True, 'label':_("Your name"), 'value':Partner.name or ""}, + 'email': {'required':True, 'readonly':True, 'label':_("Email"), 'value':Partner.email or ""}, + 'phone': {'label':_("Phone"), 'value':Partner.phone or ""}, + 'street': {'label':_("Street"), 'value':Partner.street or ""}, + 'street2': {'label':_("Street2"), 'value':Partner.street2 or ""}, + 'city': {'label':_("City"), 'value':Partner.city or ""}, + 'zip': {'label':_("Zip / Postal Code"), 'value':Partner.zip or ""}, + 'country_id': {'label':_("Country"), 'value':Partner.country_id and Partner.country_id.id or ""}, + 'state_id': {'label':_("State"), 'value':Partner.state_id and Partner.state_id.id or ""}, + } + else: + result = {'success':False, 'message':'Account not found !!!'} + response.update(result) + return self._response('account', response) + + @route('/mobikul/localizationData', csrf=False, type='http', auth="none", methods=['POST']) + def getLocalizationData(self, **kwargs): + response = self._authenticate(False, **kwargs) + if response.get('success'): + StateObj = request.env['res.country.state'].sudo() + countries = request.env['res.country'].sudo().search_read([], fields=['name','state_ids']) + if countries: + result = {'countries':[]} + state_ids = [] + for country in countries: + states = [] + if country['state_ids']: + states = StateObj.search_read([('id','in',country['state_ids'])], fields=['name']) + result['countries'].append({ + 'id':country['id'], + 'name':country['name'], + 'states':states, + }) + else: + result = {'success':False, 'message':'Account not found !!!'} + response.update(result) + return self._response('account', response) + + @route('/mobikul/search', csrf=False, type='http', auth="none", methods=['POST']) + def getSearchData(self, **kwargs): + response = self._authenticate(False, **kwargs) + Mobikul = request.env['mobikul'].sudo() + if self.auth: + result = Mobikul.authenticate(self._lcred, True, self._sLogin, context={'base_url':self.base_url}) + response.update(result) + if response.get('success'): + self._mData.update(response.get('local',{})) + result = Mobikul.fetch_products(**self._mData) + response.update(result) + if response.get('addons',{}).get('website_sale_wishlist') and response.get('customerId'): + response['wishlist'] = self._website_Wishlist(response.get('customerId')) + return self._response('search', response) + + def _website_Wishlist(self, customer_id, product_id=False): + result = [] + wishlists = request.env['product.wishlist'].sudo().search([('partner_id','=',customer_id)]) + for wishlist in wishlists: + result.append(wishlist.product_id.id) + return result + + @route('/mobikul/template/', csrf=False, type='http', auth="none", methods=['POST']) + def getTemplateData(self, template_id, **kwargs): + response = self._authenticate(False, **kwargs) + if self.auth: + result = request.env['mobikul'].sudo().authenticate(self._lcred, True, self._sLogin, context={'base_url':self.base_url}) + response.update(result) + if response.get('success'): + wishlist = [] + if response.get('addons',{}).get('website_sale_wishlist') and response.get('customerId'): + wishlist = self._website_Wishlist(response.get('customerId',0)) + TemplateObj = request.env['product.template'].sudo() + Template = TemplateObj.search([('id','=',template_id)]) + if Template: + result = { + 'templateId' :Template.id, + 'name' :Template.name or "", + 'attributes' :[], + "images": [] + } + local = response.get('local',{}) + if response.get('addons',{}).get('review'): + result.update({ + 'avg_rating':Template.avg_review(), + 'total_review':len(Template.fetch_active_review(Template.id)) + }) + if response.get('addons',{}).get('odoo_marketplace'): + if Template.marketplace_seller_id and Template.marketplace_seller_id.website_published: + result.update({ + 'seller_info':{ + 'seller_profile_url' :"/myTemplateseller/%s"%Template.marketplace_seller_id.id, + 'marketplace_seller_id' :Template.marketplace_seller_id.id, + 'seller_name' :Template.marketplace_seller_id.name, + 'seller_profile_image' : self.get_marketplace_image_url(self.base_url, 'res.partner', Template.marketplace_seller_id.id,'profile_image'), + 'average_rating' : Template.marketplace_seller_id.avg_review(), + 'total_reviews' :len(Template.marketplace_seller_id.seller_review_ids.filtered(lambda r: (r.active == True and r.state == "pub"))), + 'message' :str(Template.marketplace_seller_id.total_active_recommendation()[1])+" positive feedback (%s ratings)"%Template.marketplace_seller_id.avg_review() + } + }) + else: + result.update({ + 'seller_info': None + }) + + for im in Template.product_image_ids: + result['images'].append(_get_image_url(self.base_url, 'product.image', im.id,'image')) + for ali in Template.attribute_line_ids: + temp = { + "name":ali.attribute_id.name or "", + "attributeId":ali.attribute_id.id, + "type":ali.attribute_id.type, + "newVariant":ali.attribute_id.create_variant, + "values":[] + } + for v in ali.value_ids: + temp["values"].append({ + "name":v.name or "", + "valueId":v.id, + "htmlCode":v.html_color or "", + "newVariant":ali.attribute_id.create_variant, + }) + result['attributes'].append(temp) + if Template.product_variant_count > 1: + result.update({ + 'priceUnit' :_displayWithCurrency(local.get('lang_obj'),Template.product_variant_id.lst_price, local.get('currencySymbol'), local.get('currencyPosition')), + 'priceReduce' :Template.product_variant_id.price < Template.product_variant_id.lst_price and _displayWithCurrency(local.get('lang_obj'),Template.product_variant_id.price, local.get('currencySymbol'), local.get('currencyPosition')) or "", + 'productId' :Template.product_variant_id.id, + 'productCount' :Template.product_variant_count, + 'description' :Template.product_variant_id.description_sale or "", + 'thumbNail' :_get_image_url(self.base_url, 'product.product', Template.product_variant_id.id,'image'), + 'images' :[_get_image_url(self.base_url, 'product.product', Template.product_variant_id.id,'image')], + 'variants' :[] + }) + for var in Template.product_variant_ids: + temp = { + "productId":var.id, + 'images':[_get_image_url(self.base_url, 'product.product', var.id,'image')], + 'priceReduce':var.price < var.lst_price and _displayWithCurrency(local.get('lang_obj'),var.price, local.get('currencySymbol'), local.get('currencyPosition')) or "", + 'priceUnit':_displayWithCurrency(local.get('lang_obj'),var.lst_price, local.get('currencySymbol'), local.get('currencyPosition')), + "combinations":[], + "addedToWishlist":var.id in wishlist, + } + for avl in var.attribute_value_ids: + temp["combinations"].append({ + "valueId":avl.id, + "attributeId":avl.attribute_id and avl.attribute_id.id, + }) + result['variants'].append(temp) + else: + result.update({ + 'priceUnit' :_displayWithCurrency(local.get('lang_obj'),Template.lst_price, local.get('currencySymbol'), local.get('currencyPosition')), + 'priceReduce' :Template.price < Template.lst_price and _displayWithCurrency(local.get('lang_obj'),Template.price, local.get('currencySymbol'), local.get('currencyPosition')) or "", + 'productId' :Template.product_variant_id and Template.product_variant_id.id or '', + 'productCount' :Template.product_variant_count, + 'description' :Template.description_sale or "", + 'thumbNail' :_get_image_url(self.base_url, 'product.template', Template.id,'image'), + 'images' :[_get_image_url(self.base_url, 'product.template', Template.id,'image')], + "addedToWishlist":Template.product_variant_id.id in wishlist + }) + else: + result = {'success':False, 'message':'Template not found !!!'} + response.update(result) + return self._response('template', response) + + @route(['/mobikul/mycart','/mobikul/mycart/'], csrf=False, type='http', auth="none", methods=['POST','PUT','DELETE']) + def getMyCart(self, line_id=0, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + result = {} + PartnerObj = request.env['res.partner'].sudo() + Partner = PartnerObj.browse(response.get('customerId')) + if Partner: + if request.httprequest.method == "POST": + last_order = Partner.last_website_so_id + if last_order: + local = response.get('local',{}) + result = { + "name":last_order.name, + "subtotal":{"title":"Subtotal", + "value":_displayWithCurrency(local.get('lang_obj'),last_order.amount_untaxed, local.get('currencySymbol'), local.get('currencyPosition')), + }, + "tax":{"title":"Taxes", + "value":_displayWithCurrency(local.get('lang_obj'),last_order.amount_tax, local.get('currencySymbol'), local.get('currencyPosition')), + }, + "grandtotal":{"title":"Total", + "value":_displayWithCurrency(local.get('lang_obj'),last_order.amount_total, local.get('currencySymbol'), local.get('currencyPosition')), + }, + "items":[] + } + for item in last_order.order_line: + if response.get('addons', {}).get('website_sale_delivery') and item.is_delivery: + shippingMethod = { + "tax":[tax.name for tax in item.tax_id], + "name":item.order_id.carrier_id.name, + "description":item.order_id.carrier_id.website_description or "", + "shippingId":item.order_id.carrier_id.id, + "total": _displayWithCurrency(local.get('lang_obj'), item.price_subtotal, + local.get('currencySymbol'), local.get('currencyPosition')), + } + result.update({"delivery":shippingMethod}) + else: + temp = { + "lineId":item.id, + "templateId":item.product_id and item.product_id.product_tmpl_id.id or "", + "name":item.product_id and item.product_id.display_name or item.name, + "thumbNail":_get_image_url(self.base_url, 'product.product', item.product_id and item.product_id.id or "",'image'), + "priceReduce":item.price_reduce < item.price_unit and _displayWithCurrency(local.get('lang_obj'),item.price_reduce, local.get('currencySymbol'), local.get('currencyPosition')) or "", + "priceUnit":_displayWithCurrency(local.get('lang_obj'),item.price_unit, local.get('currencySymbol'), local.get('currencyPosition')), + "qty":item.product_uom_qty, + "total":_displayWithCurrency(local.get('lang_obj'),item.price_subtotal, local.get('currencySymbol'), local.get('currencyPosition')), + "discount":item.discount and "(%d%% OFF)"%item.discount or "", + } + result['items'].append(temp) + if not len(result['items']): + result['message'] = _('Your Shopping Bag is empty.') + else: + result = {'message':_('Your Shopping Bag is empty.')} + else: + OrderLineObj = request.env['sale.order.line'].sudo() + OrderLine = OrderLineObj.search([('id','=',line_id)]) + if OrderLine: + if request.httprequest.method == "PUT": + result = {'message':'Updated successfully.'} + if self._mData.get('set_qty'): + OrderLine.product_uom_qty = self._mData.get('set_qty') + elif self._mData.get('add_qty'): + OrderLine.product_uom_qty +=int(self._mData['add_qty']) + else: + result = {'message':'Wrong request.'} + elif request.httprequest.method == "DELETE": + try: + result = {'message':'%s'%(OrderLine.product_id and OrderLine.product_id.name or OrderLine.name)+_(' was removed from your Shopping Bag.')} + OrderLine.unlink() + except: + result = {'message':'Please try again after some time.'} + else: + result = {'message':'Wrong request.'} + else: + result = {'message':'No matching product found !!!'} + else: + result = {'success':False, 'message':'Account not found !!!'} + response.update(result) + return self._response('cart', response) + + @route('/mobikul/mycart/setToEmpty', csrf=False, type='http', auth="none", methods=['DELETE']) + def setToEmpty(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + result = {} + PartnerObj = request.env['res.partner'].sudo() + Partner = PartnerObj.browse(response.get('customerId')) + if Partner: + last_order = Partner.last_website_so_id + if last_order: + try: + result = {'message':_('Your Shopping Bag has been set to Empty.')} + last_order.order_line.unlink() + result['cartCount'] = last_order.cart_count + except: + result = {'message':'Please try again after some time.'} + else: + result = {'message':_('Your Shopping Bag is already empty.')} + else: + result = {'success':False, 'message':'Account not found !!!'} + response.update(result) + return self._response('setToEmpty', response) + + @route('/mobikul/mycart/addToCart', csrf=False, type='http', auth="none", methods=['POST']) + def addToCart(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + result = {} + Mobikul = request.env['mobikul'].sudo() + result = Mobikul.add_to_cart(response.get('customerId'),self._mData.get("productId"),self._mData.get("set_qty"),self._mData.get("add_qty"),response) + response.update(result) + return self._response('addToCart', response) + + @route('/mobikul/paymentAcquirers', csrf=False, type='http', auth="none", methods=['POST']) + def getPaymentAcquirer(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + result = {} + AcquirerObj = request.env['payment.acquirer'].sudo() + Acquirers = AcquirerObj.search_read([('is_mobikul_available','=',1),('mobikul_reference_code','in',AQUIRER_REF_CODES)], fields=['name','pre_msg','mobikul_reference_code']) + if Acquirers: + result = {'acquirers':Acquirers} + for index,value in enumerate(result['acquirers']): + result['acquirers'][index]['thumbNail'] = _get_image_url(self.base_url, 'payment.acquirer', result['acquirers'][index]['id'],'image') + result['acquirers'][index]['description'] = remove_htmltags(result['acquirers'][index].pop('pre_msg')) or "" + result['acquirers'][index]['code'] = result['acquirers'][index].pop('mobikul_reference_code') or "" + else: + result = {'success':False, 'message':'No Active Payment methods found.'} + response.update(result) + return self._response('paymentAcquirer', response) + + def _computePayfortSdkSignature(self,values,Acquirer): + keys = values.keys() + keys.sort() + sign = "" + for k in keys: + sign = sign + k + "=" +str(values[k]) + sign = Acquirer.request_phrase + sign + Acquirer.request_phrase + sha256sign = hashlib.sha256(sign.encode()).hexdigest() + return sha256sign + + def _computePayfortPaymentToken(self,val,Acquirer): + token_url = PAYFORT_SDK_ENV_URL.get(Acquirer.environment) + result = requests.post(url= token_url, json=val) + return result.json() + + def _computePayfortSdkToken(self,Acquirer,Transaction,order_name): + if self._mData.get('device_id'): + val = { + "service_command":"SDK_TOKEN", + "access_code":Acquirer.access_code, + "merchant_identifier":Acquirer.merchant_identifier, + "language":"en", + "device_id":self._mData.get('device_id'), + } + val['signature'] = self._computePayfortSdkSignature(val,Acquirer) + sdkToken = self._computePayfortPaymentToken(val,Acquirer) + return { + 'status':True, + 'paymentReference':_get_next_reference(order_name), + 'code':'PAYFORT', + 'auth':True, + "sdkToken":sdkToken + } + else: + return { + 'status':False, + 'message': "No Device Id Found !!" + } + + def _getAquirerCredentials(self, order_name, Acquirer,response): + if Acquirer.mobikul_reference_code == 'COD': + return {'status':True,'code':'COD','auth':False} + elif Acquirer.mobikul_reference_code == 'STRIPE_W': + Transaction = request.env['payment.transaction'].sudo() + return {'status':True,'paymentReference':Transaction.get_next_reference(order_name),'code':'STRIPE','auth':True,'secret_key':Acquirer.stripe_checkout_client_secret_key,'publishable_key':Acquirer.stripe_checkout_publishable_key} + elif Acquirer.mobikul_reference_code == 'PAYFORT': + Transaction = request.env['payment.transaction'].sudo() + sdkTokenResponse = self._computePayfortSdkToken(Acquirer,Transaction,order_name) + return sdkTokenResponse + elif Acquirer.mobikul_reference_code == 'STRIPE_E': + Transaction = request.env['payment.transaction'].sudo() + return {'status':True,'paymentReference':Transaction.get_next_reference(order_name),'code':'STRIPE','auth':True,'secret_key':Acquirer.stripe_secret_key,'publishable_key':Acquirer.stripe_publishable_key} + elif Acquirer.mobikul_reference_code == '2_CHECKOUT': + Transaction_refNo = request.env['payment.transaction'].sudo().get_next_reference(order_name) + paymentUrl = "%sapp/payment/2checkout?reference=%s&acquirer_id=%s"%(self.base_url,order_name,Acquirer.id) + return {'status':True,'paymentUrl':paymentUrl,'code':'2_CHECKOUT','auth':True,"acquire":Acquirer.name} + elif Acquirer.mobikul_reference_code == 'PAYFORT_SADAD': + Transaction_refNo = request.env['payment.transaction'].sudo().get_next_reference(order_name) + paymentUrl = "%sapp/payment/payfortsadad?reference=%s&acquirer_id=%s&sadad_olp="%(self.base_url,order_name,Acquirer.id) + return {'status':True,'paymentUrl':paymentUrl,'code':'PAYFORT_SADAD','auth':True,"acquire":Acquirer.name} + else: + return {'status':False,'message':_('Payment Mode not Available.')} + + def _getAquirerState(self, Acquirer, status=False): + if Acquirer.mobikul_reference_code in ['COD']: + return "pending" + elif Acquirer.mobikul_reference_code in ['STRIPE_W','STRIPE_E']: + return STATUS_MAPPING['STRIPE'].get(status,'pending') + else: + return "pending" + + + def _orderReview(self,user,response,Acquirer): + last_order = user.partner_id.last_website_so_id + if last_order and len(last_order.order_line): + local = response.get('local',{}) + if self._mData.get('shippingAddressId'): + last_order.partner_shipping_id = int(self._mData.get('shippingAddressId')) + # add shippigMethod + if response.get('addons', {}).get('website_sale_delivery') and self._mData.get("shippingId"): + last_order.sudo()._check_carrier_quotation( force_carrier_id=int(self._mData.get("shippingId"))) + + result = { + "name":last_order.name, + "billingAddress": last_order.partner_invoice_id._display_address(), + "shippingAddress": last_order.partner_shipping_id._display_address(), + "paymentAcquirer": Acquirer.name, + # "paymentPreMessage": Acquirer.pre_msg, + "subtotal":{"title":"Subtotal", + "value":_displayWithCurrency(local.get('lang_obj'),last_order.amount_untaxed, local.get('currencySymbol'), local.get('currencyPosition')), + }, + "tax":{"title":"Taxes", + "value":_displayWithCurrency(local.get('lang_obj'),last_order.amount_tax, local.get('currencySymbol'), local.get('currencyPosition')), + }, + "grandtotal":{"title":"Total", + "value":_displayWithCurrency(local.get('lang_obj'),last_order.amount_total, local.get('currencySymbol'), local.get('currencyPosition')), + }, + "amount":last_order.amount_total, + "currency":last_order.pricelist_id.currency_id.name or "", + "items":[], + } + + for item in last_order.order_line: + if response.get('addons', {}).get('website_sale_delivery') and item.is_delivery: + shippingMethod = { + "tax":[tax.name for tax in item.tax_id], + "name":item.order_id.carrier_id.name, + "description":item.order_id.carrier_id.website_description or "", + "shippingId":item.order_id.carrier_id.id, + "total": _displayWithCurrency(local.get('lang_obj'), item.price_subtotal, + local.get('currencySymbol'), local.get('currencyPosition')), + } + result.update({"delivery":shippingMethod}) + else: + temp = { + "lineId":item.id, + "templateId":item.product_id and item.product_id.product_tmpl_id.id or "", + "name":item.product_id and item.product_id.display_name or item.name, + "thumbNail":_get_image_url(self.base_url, 'product.product', item.product_id and item.product_id.id or "",'image'), + "priceReduce":item.price_reduce < item.price_unit and _displayWithCurrency(local.get('lang_obj'),item.price_reduce, local.get('currencySymbol'), local.get('currencyPosition')) or "", + "priceUnit":_displayWithCurrency(local.get('lang_obj'),item.price_unit, local.get('currencySymbol'), local.get('currencyPosition')), + "qty":item.product_uom_qty, + "total":_displayWithCurrency(local.get('lang_obj'),item.price_subtotal, local.get('currencySymbol'), local.get('currencyPosition')), + "discount":item.discount and "(%d%% OFF)"%item.discount or "", + } + result['items'].append(temp) + + result['paymentData'] = self._getAquirerCredentials(last_order.name, Acquirer,response) + result['paymentData'].update({'customer_email':last_order.partner_id.email}) + last_order.payment_acquirer_id = Acquirer.id + else: + result = {'success':False, 'message':_('Add some products in order to proceed.')} + return result + + @route('/mobikul/orderReviewData', csrf=False, type='http', auth="none", methods=['POST']) + def getOrderReviewData(self, **kwargs): + Mobikul = request.env['mobikul'].sudo() + response = self._authenticate(True, **kwargs) + if response.get('success'): + result = {} + Acquirer = request.env['payment.acquirer'].sudo().browse(int(self._mData.get('acquirerId'))) + if Acquirer: + UserObj = request.env['res.users'].sudo() + user = UserObj.browse(response.get('userId',0)) + if user: + if response.get('addons',{}).get('email_verification') and Mobikul.email_verification_defaults().get('restrict_unverified_users'): + if user.wk_token_verified: + result = self._orderReview(user,response,Acquirer) + else: + result = {'success':False,'message':_("You can't place your order, please verify your account") } + else: + result = self._orderReview(user,response,Acquirer) + else: + result = {'success':False, 'message':_('Account not found !!!')} + else: + result = {'success':False, 'message':_('No Payment methods found with given id.')} + response.update(result) + return self._response('orderReviewData', response) + + + @route('/mobikul/placeMyOrder', csrf=False, type='http', auth="none", methods=['POST']) + def placeMyOrder(self, **kwargs): + response = self._authenticate(True, **kwargs) + result={} + if response.get('success'): + result = self.placeOrder(response.get('customerId')) + response.update(result) + return self._response('placeMyOrder', response) + + def _sendPaymentAcknowledge(self,last_order,Partner,customerId,result): + if last_order.payment_tx_id.state != 'error': + last_order.with_context(send_email=True).action_confirm() + Partner.last_website_so_id = False + self._pushNotification(self._mData.get("fcmToken", ""), condition='orderplaced', + customer_id=customerId) + result.update({ + 'url':"/mobikul/my/order/%s"%last_order.id, + 'name':last_order.name, + 'cartCount': 0, + 'success': True, + 'message': 'Your order' + ' %s ' % (last_order.name) + 'has been placed successfully.', + }) + if last_order.payment_tx_id.state in ['pending','draft']: + result.update({'txn_msg': remove_htmltags(last_order.payment_acquirer_id.pending_msg)}) + elif last_order.payment_tx_id.state == 'done': + result.update({'txn_msg':remove_htmltags(last_order.payment_acquirer_id.done_msg)}) + elif last_order.payment_tx_id.state == 'cancel': + result.update({'txn_msg':remove_htmltags(last_order.payment_acquirer_id.cancel_msg)}) + else: + result.update({'txn_msg':'No transaction state found..'}) + + else: + result.update({ + 'success': False, + 'message': "ERROR", + 'txn_msg':last_order.payment_acquirer_id.error_msg or "ERROR" + }) + return result + + def placeOrder(self,customerId): + result = {} + PartnerObj = request.env['res.partner'].sudo() + Partner = PartnerObj.browse(customerId) + if Partner: + last_order = Partner.last_website_so_id + if last_order: + if last_order.payment_acquirer_id: + if last_order.payment_acquirer_id.name in ["2Checkout","PAYFORT_SADAD"]: + result1 = { + 'paymentReference':"%s"%last_order.payment_tx_id.reference, + 'paymentStatus':"%s"%last_order.payment_tx_id.state, + } + result = self._sendPaymentAcknowledge(last_order,Partner,customerId,result) + result.update(result1) + else: + Transaction = request.env['payment.transaction'].sudo() + tx_values = { + 'acquirer_id': last_order.payment_acquirer_id.id, + 'type': 'form', + 'amount': last_order.amount_total, + 'currency_id': last_order.pricelist_id.currency_id.id, + 'partner_id': last_order.partner_id.id, + 'partner_country_id': last_order.partner_id.country_id.id, + 'reference': self._mData.get('paymentReference',Transaction.get_next_reference(last_order.name)), #ptptpt + 'sale_order_id': last_order.id, + 'state': self._getAquirerState(last_order.payment_acquirer_id,self._mData.get('paymentStatus')), #ptptpt + 'acquirer_reference': 'MOBIKUL', + } + tx = Transaction.create(tx_values) + # update quotation + last_order.write({ + 'payment_tx_id': tx.id, + }) + result = self._sendPaymentAcknowledge(last_order,Partner,customerId,{}) + else: + result = {'success':False, 'message':_('No Payment Method found.')} + else: + result = {'success':False, 'message':_('Add some products in order to proceed.')} + else: + result = {'success':False, 'message':('Account not found !!!')} + return result + + @route('/mobikul/saveMyDetails', csrf=False, type='http', auth="none", methods=['POST']) + def saveMyDetails(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + result = {} + Userobj = request.env['res.users'].sudo() + User = Userobj.browse(response.get('userId')) + if User: + result['message'] = _("Updated Successfully.") + if self._mData.get('image'): + try: + User.write({'image':self._mData['image']}) + result['customerProfileImage'] = _get_image_url(self.base_url, 'res.partner', User.partner_id.id, 'image') + except Exception as e: + result['message'] = _("Please try again later")+" %r"%e + if self._mData.get('name'): + User.write({'name':self._mData['name']}) + if self._mData.get('password'): + User.write({'password':self._mData['password']}) + else: + result = {'success':False, 'message':_('Account not found !!!')} + response.update(result) + return self._response('saveMyDetails', response) + + def _tokenUpdate(self, customer_id=False): + FcmRegister = request.env['fcm.registered.devices'].sudo() + already_registered = FcmRegister.search([('device_id','=',self._mData.get("fcmDeviceId"))]) + if already_registered: + already_registered.write({'token':self._mData.get("fcmToken"),'customer_id':customer_id}) + else: + FcmRegister.create({ + 'token':self._mData.get("fcmToken",""), + 'device_id':self._mData.get("fcmDeviceId",""), + 'description':"%r"%self._mData, + 'customer_id':customer_id, + }) + return True + + def _pushNotification(self, token, condition='signup', customer_id=False): + notifications = request.env['mobikul.push.notification.template'].sudo().search([('condition','=',condition)]) + for n in notifications: + n._send({'to':token},customer_id) + return True + + @route('/mobikul/registerFcmToken', csrf=False, type='http', auth="none", methods=['POST']) + def registerFcmToken(self, **kwargs): + response = self._authenticate(False, **kwargs) + if response.get('success'): + customer_id = False + if self._mData.get("customerId") and request.env['res.partner'].sudo().browse(int(self._mData["customerId"])).exists(): + customer_id = int(self._mData["customerId"]) + self._tokenUpdate(customer_id=customer_id) + response.update({'message':_('Request completed !')}) + return self._response('registerFcmToken', response) + + + @route('/mobikul/notificationMessages', csrf=False, type='http', auth="none", methods=['POST']) + def getNotificationMessages(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + PartnerObj = request.env['res.partner'].sudo() + Partner = PartnerObj.browse(response.get('customerId')) + fields = ['id','name','create_date','title','subtitle','body','banner','icon','period','datatype','is_read'] + domain = [('customer_id','=',Partner.id)] + Message = request.env['mobikul.notification.messages'].sudo() + notification_message = Message.search_read(domain, limit=self._mData.get('limit',response.get('itemsPerPage',5)), offset=self._mData.get('offset',0), order="id desc", fields=fields) + for msg in notification_message: + msg['name'] = msg['name'] or "" + msg['title'] = msg['title'] or "" + msg['subtitle'] = msg['subtitle'] or "" + msg['body'] = msg['body'] or "" + msg['icon'] = _get_image_url(self.base_url, 'mobikul.notification.messages', msg['id'] ,'icon') + msg['banner'] = _get_image_url(self.base_url, 'mobikul.notification.messages', msg['id'] ,'banner') + result = {'all_notification_messages':notification_message} + response.update(result) + + return self._response('notificationMessages', response) + + + @route('/mobikul/notificationMessage/', csrf=False, type='http', auth="none", methods=['POST','PUT','DELETE']) + def getNotificationMessageDetails(self, message_id, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + MessageObj = request.env['mobikul.notification.messages'].sudo() + message = MessageObj.search([('id','=',message_id),('customer_id','=',response.get('customerId'))]) + if message: + if request.httprequest.method == "POST": + message.is_read = True + result = { + 'id':message.id, + 'name':message.name or "", + 'create_date':message.create_date , + 'title':message.title or "", + 'subtitle':message.subtitle or "", + 'body':message.body or "", + 'icon':_get_image_url(self.base_url, 'mobikul.notification.messages', message.id ,'icon'), + 'banner':_get_image_url(self.base_url, 'mobikul.notification.messages', message.id ,'banner'), + 'period':message.period, + 'is_read':message.is_read, + 'datatype':message.datatype, + 'success':True, + 'message':'Successfull' + } + elif request.httprequest.method == "DELETE": + message.active = False + result = {'success':True, 'message':_('Deleted Successfully')} + elif request.httprequest.method == "PUT": + message.is_read = self._mData.get('is_read',message.is_read) + result = {'success':True, 'message':_('Updated Successfully')} + else: + result = {'success':False, 'message':_('Message not Found')} + response.update(result) + return self._response('notificationMessageDetails', response) + + +# Wishlist api is according website_sale_wishlist + + @route('/mobikul/my/wishlists', csrf=False, type='http', auth="none", methods=['POST']) + def getMyWishlists(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + if response.get('addons',{}).get('website_sale_wishlist') and response.get('customerId'): + wishlists=[] + Partner = request.env['res.partner'].sudo().browse(response.get('customerId')) + local = response.get('local',{}) + for wishlist in Partner.wishlist_ids: + if wishlist.product_id: + product_detail = { + 'id':wishlist.id, + "name": wishlist.product_id.name, + "thumbNail": _get_image_url(self.base_url, 'product.product', wishlist.product_id.id, 'image'), + # "priceReduce":Product.price_reduce < Product.price_unit and _displayWithCurrency(local.get('lang_obj'),Product.price_reduce, local.get('currencySymbol'), local.get('currencyPosition')) or "", + "priceUnit":_displayWithCurrency(local.get('lang_obj'),wishlist.product_id.lst_price, local.get('currencySymbol'), local.get('currencyPosition')), + # "productId": wishlist.product_id.product_tmpl_id.id, + "productId": wishlist.product_id.id, + + } + wishlists.append(product_detail) + result = { + 'success':True, + "wishLists":wishlists, + 'message':'SUCCESS' + } + else: + result = {'success':False, 'message':'Wishlist is not Active !!!'} + response.update(result) + return self._response('myWishlists', response) + + @route('/my/removeWishlist/', csrf=False, type='http', auth="none", methods=['DELETE']) + def removeWishlist(self, wishlist_id, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + if response.get('addons',{}).get('website_sale_wishlist') and response.get('customerId'): + try: + wishlist = request.env['product.wishlist'].sudo().search([('id','=',wishlist_id)]) + if wishlist: + wishlist.unlink() + result={'success':True, + 'message':'Item removed' + } + else: + result={ + 'success':False, + 'message':'Not Found', + } + except Exception as e: + result={ + 'success':False, + 'message':'Please try again later', + 'detail':'Error Details: %r'%e, + } + else: + result = {'success':False, 'message':'Wishlist is not Active !!!'} + response.update(result) + return self._response('removeWishlist', response) + + @route('/my/removeFromWishlist/', csrf=False, type='http', auth="none", methods=['DELETE']) + def removeFromWishlist(self, product_id, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + if response.get('addons',{}).get('website_sale_wishlist') and response.get('customerId'): + try: + wishlist = request.env['product.wishlist'].sudo().search([('product_id','=',product_id),('partner_id','=',response.get('customerId'))]) + if wishlist: + wishlist.unlink() + result={'success':True, + 'message':'Item removed' + } + else: + result={ + 'success':False, + 'message':'Not Found', + } + except Exception as e: + result={ + 'success':False, + 'message':'Please try again later', + 'detail':'Error Details: %r'%e, + } + else: + result = {'success':False, 'message':'Wishlist is not Active !!!'} + response.update(result) + return self._response('removeFromWishlist', response) + + @route('/my/addToWishlist', csrf=False, type='http', auth="none", methods=['POST']) + def addToWishlist(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + if response.get('addons',{}).get('website_sale_wishlist') and response.get('customerId'): + result = self.add2Ws(response.get('customerId'), self._mData.get("productId")) + else: + result = {'success':False, 'message':'Wishlist is not Active !!!'} + response.update(result) + return self._response('addToWishlist', response) + + def add2Ws(self, customer_id, product_id): + Mobikul = request.env['mobikul'].sudo().search([], limit=1) + wishlistObj = request.env['product.wishlist'].sudo() + p = request.env['product.product'].sudo().browse(product_id) + try: + wishlistObj._add_to_wishlist(Mobikul.pricelist_id.id , Mobikul.currency_id.id , Mobikul.website_id.id, p.website_price , product_id, partner_id=customer_id, session=False) + result = {'success':True, + 'message':"Item moved to Wishlist" + } + except Exception as e: + result = { + 'success':False, + 'message':'Please try again later', + 'detail':'Error Details: %r'%e, + } + return result + + + @route('/my/wishlistToCart', csrf=False, type='http', auth="none", methods=['POST']) + def moveWishlistToCart(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + if response.get('addons',{}).get('website_sale_wishlist') and response.get('customerId'): + wishlistObj = request.env['product.wishlist'].sudo() + wishlist = wishlistObj.search([('id','=',self._mData.get("wishlistId")),('partner_id','=',response.get('customerId'))]) + result = request.env['mobikul'].sudo().add_to_cart(response.get('customerId'),wishlist.product_id.id, False, self._mData.get("add_qty",1), response) + if result.get("success"): + wishlist.unlink() + result = {'success':True, 'message':'Item moved to Bag'} + else: + result = {'success':False, 'message':'Please try again later'} + else: + result = {'success':False, 'message':'Wishlist is not Active !!!'} + response.update(result) + return self._response('moveWishlistToCart', response) + + + @route('/my/cartToWishlist', csrf=False, type='http', auth="none", methods=['POST']) + def moveCartToWishlist(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + if response.get('addons',{}).get('website_sale_wishlist') and response.get('customerId'): + OrderLine = request.env['sale.order.line'].sudo().search([('id','=',self._mData.get('line_id'))]) + if OrderLine: + result = self.add2Ws(response.get('customerId'), OrderLine.product_id.id) + if result.get("success"): + OrderLine.unlink() + else: + result = {'success':False, 'message':'Order not found'} + else: + result = {'success':False, 'message':'Wishlist is not Active !!!'} + response.update(result) + return self._response('moveCartToWishlist', response) + + # @route('/mobikul/my/reviewList', csrf=False, type='http', auth="none", methods=['GET']) + # def getReviewList(self, **kwargs): + # response = self._authenticate(True, **kwargs) + # if response.get('success'): + # if response.get('addons',{}).get('review') and response.get('customerId'): + # result = {} + # PartnerObj = request.env['res.partner'].sudo() + # Partner = PartnerObj.browse(response.get('customerId')) + # if Partner: + # reviewList = [] + # pass + # # write the logic for get customer review + # result = {'all_ReviewList':reviewList} + # else: + # result = {'success':False, 'message':'Account not found !!!'} + # else: + # response.update({'success':False, 'message':'Review Module not install !!!'}) + # response.update(result) + # return self._response('reviewList', response) + + @route('/product/reviews', csrf=False, type='http', auth="none", methods=['POST']) + def getProductReview(self, **kwargs): + response = self._authenticate(False, **kwargs) + if response.get('success'): + if response.get('addons',{}).get('review'): + product_reviews = [] + reviewObj = request.env['user.review'].sudo() + domain = [('template_id','=',self._mData.get('template_id')),('state','=','pub')] + fields =['customer','customer_image','email','likes','dislikes','rating','title','msg','create_date'] + product_reviews = reviewObj.search_read(domain, limit=self._mData.get('limit',response.get('itemsPerPage',5)), offset=self._mData.get('offset',0), order="id desc", fields=fields) + for review in product_reviews: + review['customer_image'] = _get_image_url(self.base_url, 'user.review', review['id'] ,'customer_image') + review['create_date'] = request.env['mobikul'].sudo().easy_date(review['create_date']) + result = {'product_reviews':product_reviews,"reviewCount":len(product_reviews)} + + else: + result = {'success':False, 'message':_('Review Module not install !!!')} + response.update(result) + return self._response('ProductReview', response) + + @route('/my/saveReview', csrf=False, type='http', auth="none", methods=['POST']) + def addReview(self, **kwargs): + response = self._authenticate(True, **kwargs) + Mobikul = request.env['mobikul'].sudo() + if response.get('success'): + result = {} + if response.get('addons',{}).get('review') and response.get('customerId'): + Partner = request.env['res.partner'].sudo().browse(response.get('customerId')) + if Partner: + vals={ + "title":self._mData.get("title"), + 'msg':self._mData.get("detail"), + "rating": self._mData.get("rate"), + 'partner_id':Partner.id, + "template_id": self._mData.get("template_id"), + "customer":Partner.name, + "email":Partner.email, + "customer_image":Partner.image + } + try: + request.env['user.review'].sudo().create(vals) + if Mobikul.review_defaults().get('auto_publish'): + result = {'success':True, 'message':_('Thanks for your review.')} + else: + result = {'success':True, 'message': Mobikul.review_defaults().get('message_when_unpublish')} + except Exception as e: + result = {'success':False, 'message':_('Please try again later')} + else: + result = {'success':False, 'message':_('Account not found !!!')} + else: + response.update({'success':False, 'message':_('Review Module not install !!!')}) + response.update(result) + return self._response('addReview', response) + + + + @route('/send/verifyEmail', csrf=False, type='http', auth="none", methods=['POST']) + def verifyEmail(self, **kwargs): + response = self._authenticate(True, **kwargs) + Mobikul = request.env['mobikul'].sudo() + if response.get('success'): + result={} + if response.get('addons',{}).get('email_verification') and Mobikul.email_verification_defaults().get('restrict_unverified_users'): + UserObj = request.env['res.users'].sudo() + user = UserObj.search([('id','=',response.get('userId'))]) + if not user.wk_token_verified: + UserObj.send_verification_email(user.id) + response['message']=_("Verification email sent successfully.") + response['success'] = True + else: + response['message']=_("Email already verified.") + response['success'] = False + else: + response.update({'success':False, 'message':_('Email Verification Module not install !!!')}) + response.update(result) + return self._response('verifyEmail', response) + + + @route('/review/likeDislike', csrf=False, type='http', auth="none", methods=['POST']) + def addLikeDislike(self, **kwargs): + response = self._authenticate(True, **kwargs) + likeDislikeObj = request.env['review.like.dislike'].sudo() + # it assume that review module is installed + if response.get('success') and self._mData.get("review_id"): + review = request.env['user.review'].sudo().browse(self._mData.get("review_id")) + if review: + ld_exist = likeDislikeObj.search([("customer_id","=",response.get('userId')),("review_id","=",review.id)]) + vals={ + "customer_id":response.get('userId'), + 'like':self._mData.get("ishelpful"), + "dislike": not self._mData.get("ishelpful"), + 'review_id':review.id, + } + if ld_exist: + try: + ld_exist.write(vals) + result = {'success':True, 'message':_('Thank you for your feedback.')} + except Exception as e: + result = {'success':False, 'message':_('Please try again later')} + else: + try: + likeDislikeObj.sudo().create(vals) + result = {'success':True, 'message':_('Thank you for your feedback.')} + except Exception as e: + result = {'success':False, 'message':_('Please try again later')} + else: + result = {'success':False, 'message':_('Review not found !!!')} + response.update(result) + else: + response["message"] = _("You need to login first !") + return self._response('addLikeDislike', response) + + + @route('/my/Template/seller/', csrf=False, type='http', auth="none", methods=['GET']) + def productSellerInfo(self, seller_id, **kwargs): + response = self._authenticate(False, **kwargs) + if self.auth: + result = request.env['mobikul'].sudo().authenticate(self._lcred, True, self._sLogin, context={'base_url':self.base_url}) + response.update(result) + if response.get('success'): + if response.get('addons',{}).get('odoo_marketplace'): + local = response.get('local',{}) + sellerDetail = self.seller_profile_info(seller_id, local) + + if sellerDetail: + result = {'SellerInfo':sellerDetail,'success':True,'message':_('Seller Found !!!')} + else: + result = {'success':False, 'message':_('Seller Not Found !!!')} + else: + result = {'success':False, 'message':_('Marketplace is not Active !!!')} + + if response.get('addons',{}).get('wishlist') and response.get('customerId'): + wishlists = self._myWishlist(response.get('customerId',0)) + result.update({'wishlists':wishlists}) + response.update(result) + return self._response('productSellerInfo', response) + + def seller_profile_info(self,seller_id, local): + MobikulObj = request.env['mobikul'].sudo() + detail = {} + sellerDetail = request.env['res.partner'].sudo().search([('id','=',seller_id),('seller','=',True)]) + if sellerDetail: + detail = { + "seller_id" :sellerDetail.id, + 'name' :sellerDetail.name, + 'email' :sellerDetail.email, + 'average_rating' : sellerDetail.avg_review(), + 'total_reviews' :len(sellerDetail.seller_review_ids.filtered(lambda r: (r.active == True and r.state == "pub"))), + 'sales_count' :sellerDetail.seller_sales_count(), + 'product_count' :sellerDetail.seller_products_count(), + 'seller_profile_image' :self.get_marketplace_image_url(self.base_url, 'res.partner', sellerDetail.id, 'profile_image'), + 'seller_profile_banner' :self.get_marketplace_image_url(self.base_url, 'res.partner', sellerDetail.id,'profile_banner'), + 'create_date' :sellerDetail.create_date, + 'state' :sellerDetail.state_id.name or "", + 'country' :sellerDetail.country_id.name or "", + 'profile_msg' :remove_htmltags(sellerDetail.profile_msg or "") , + 'return_policy' :remove_htmltags(sellerDetail.return_policy or ""), + 'shipping_policy' :remove_htmltags(sellerDetail.shipping_policy or ""), + + } + seller_review = [] + reviews = sellerDetail.fetch_active_review2(sellerDetail.id,0,2) + seller_review = self.getSellerReviewsDetail(reviews) + detail.update({'seller_reviews':seller_review}) + context = local or {} + context.update({"domain":"[('marketplace_seller_id','=',%r)]"%sellerDetail.id, + "order":"create_date desc, id desc", + "limit":5}) + sellerProducts = MobikulObj.fetch_products(**context) + detail.update({'sellerProducts':sellerProducts}) + return detail + + def getSellerReviewsDetail(self,reviewsObj): + reviewsDetail = [] + for review in reviewsObj: + reviewsDetail.append({ + "create_date":review.create_date, + "rating":review.rating, + "not_helpful":review.not_helpful, + "total_votes":review.total_votes, + "display_name":review.display_name, + "message_is_follower":review.message_is_follower, + "title":review.title, + "id":review.id, + "msg":review.msg, + "helpful":review.helpful, + "email":review.email, + "name":review.partner_id.name, + "image":_get_image_url(self.base_url, 'res.partner', review.partner_id.id,'profile_image'), + }) + return reviewsDetail + + + # view all the product of sellers api "http://192.168.1.86:8010/mobikul/search" + # {"domain": "[('marketplace_seller_id','=',117)]", "offset": 0, "limit":100} + + @route('/mobikul/marketplace', csrf=False, type='http', auth="none", methods=['GET']) + def marketplace(self, **kwargs): + PartnerObj = request.env['res.partner'].sudo() + if request.httprequest.headers.get("Login"): + response = self._authenticate(True, **kwargs) + else: + response = self._authenticate(False, **kwargs) + if response.get('success'): + if response.get('addons',{}).get('odoo_marketplace'): + lst = [] + local = response.get('local',{}) + result = { + "banner":request.env['website'].sudo().get_mp_config_settings_values().get('landing_page_banner') or self.base_url+"odoo_marketplace/static/src/img/Hero-Banner.png", + "heading":_("Still Selling Offline? Start Selling Online."), + } + sellersObj = PartnerObj.search([('seller','=',True),('state','=','approved'),('website_published','=',True)],limit=5) + for seller in sellersObj: + sellerDetail = self.seller_profile_info(seller.id, local) + lst.append(sellerDetail) + result.update({'SellersDetail':lst,'success':True,'message':_('Marketplace page !!!')}) + else: + result = {'success':False, 'message':_('Marketplace is not Active !!!')} + response.update(result) + return self._response('marketplace', response) + + def get_marketplace_image_url(self,base_url, model_name, record_id, field_name, width=0, height=0): + """ Returns a local url that points to the image field of a given browse record only for marketplace """ + #format of marketplace image url "base_url+/ marketplace / image / 139 / res.partner / profile_banner" + if base_url and not base_url.endswith("/"): + base_url = base_url + "/" + if width or height: + return '%swebsite/image/%s/%s/%s/%sx%s' % (base_url,model_name,record_id, field_name, width, height) + else: + return '%swebsite/image/%s/%s/%s' % (base_url,model_name,record_id, field_name) + + + def checkReviewEligibility(self,seller_id,customer_id): + """ + this method is responsible for marketplace ['/seller/review/check'] controller + """ + + sol_objs = request.env["sale.order.line"].sudo().search([("product_id.marketplace_seller_id", "=", seller_id), ("order_id.partner_id", "=", customer_id), ("order_id.state", "in", ["sale", "done"])]) + for_seller_total_review_obj = request.env["seller.review"].sudo().search([('marketplace_seller_id', '=', seller_id), ('partner_id', '=', customer_id)]) + + # This code must be used in create of review + if len(sol_objs.ids) == 0: + result = {"success":False,"message" : _("You have to purchase a product of this seller first.")} + elif len(for_seller_total_review_obj.ids) >= len(sol_objs.ids): + result = {"success":False,"message" : _("According to your purchase your review limit is over.")} + else: + result = {"success":True,"message" : _("Eligible for write a review")} + return result + + @route('/my/review/seller/', csrf=False, type='http', auth="none", methods=['GET','POST']) + def reviewSeller(self, seller_id, **kwargs): + SellReviewObj = request.env['seller.review'].sudo() + response = self._authenticate(False, **kwargs) + if response.get('success'): + if response.get('addons',{}).get('odoo_marketplace'): + seller = request.env['res.partner'].sudo().search([('id','=',seller_id),('seller','=',True)]) + if seller: + if request.httprequest.method == "GET": + sellerReviewDetail = SellReviewObj.search([('marketplace_seller_id','=',seller_id),('active','=',True),('state','=','pub')]) + reviewsDetail = self.getSellerReviewsDetail(sellerReviewDetail) + result = { + 'SellerReview':reviewsDetail, + "seller_image":self.get_marketplace_image_url(self.base_url, 'res.partner',seller.id,'image'), + 'seller_profile_image': self.get_marketplace_image_url(self.base_url, 'res.partner',seller.id,'profile_image'), + 'sellerReviewCount':len(sellerReviewDetail), + 'success':True, + 'message':_('Seller Found !!!') + } + elif request.httprequest.method == "POST": + result = self.checkReviewEligibility(seller_id,response.get('customerId')) + if result.get('success'): + if self._mData.get('msg') and self._mData.get('rating') and self._mData.get('title'): + review = { + 'msg' : self._mData.get('msg'), + 'rating' : int(self._mData.get('rating')), + 'title' : self._mData.get('title'), + 'marketplace_seller_id' : seller_id, + "partner_id" : response.get('customerId'), + } + review_obj = request.env['seller.review'].sudo().create(review) + result = {'success':True,'message':_('Review create successfully for seller id')+'%s'%seller_id} + else: + result = {'success':False,'message':_('Pass the params properly for create review!!!')} + else: + result = {'success':False,'message':_('Wrong Request')} + else: + result = {'success':False,'message':_('Seller not Found.')} + + else: + result = {'success':False, 'message':'Marketplace is not Active !!!'} + response.update(result) + return self._response('reviewSeller', response) + + + @route(['/mobikul/marketplace/seller/review/vote/'], csrf=False, type='http', auth="none", methods=['POST']) + def sellerReviewVote(self, review_id, **kwargs): + review_help_obj = request.env['review.help'].sudo() + response = self._authenticate(True, **kwargs) + if response.get('success'): + if response.get('addons',{}).get('odoo_marketplace'): + ishelpful = self._mData.get('ishelpful') and "yes" or "no" + vote_exist = review_help_obj.search( [('seller_review_id', '=', review_id), ('customer_id', '=', response.get('customerId'))]) + + if vote_exist: + vote_exist[0].write({"review_help": ishelpful}) + result = {'success':True, 'message':_('seller review vote update successfully !!!')} + else: + review_help_obj.sudo().create({"customer_id": response.get('customerId'), "seller_review_id": review_id, "review_help": ishelpful}) + result = {'success':True, 'message':_('seller review vote create successfully !!!')} + else: + result = {'success':False, 'message':_('Marketplace is not Active !!!')} + response.update(result) + return self._response('sellerReviewVote', response) + + @route(['/mobikul/marketplace/seller/orderlines'], csrf=False, type='http', auth="none",methods=['POST']) + def sellerOrderLines(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + PartnerObj = request.env['res.partner'].sudo() + Partner = PartnerObj.browse(response.get('customerId')) + if Partner and Partner.seller: + if response.get('addons', {}).get('odoo_marketplace'): + result = {'success': True} + local = response.get('local', {}) + SaleOrderLine = request.env['sale.order.line'].sudo() + domain = [('marketplace_seller_id', '=',response.get('customerId'))] + if self._mData.get('state'): + domain += [('marketplace_state','=',self._mData.get('state'))] + result['tcount'] = SaleOrderLine.search_count(domain) + orderline = SaleOrderLine.search(domain, limit=self._mData.get('limit', response.get('itemsPerPage', 5)), + offset=self._mData.get('offset', 0), order="id desc") + result['sellerOrderLines'] = [] + for order in orderline: + temp = { + 'line_id': order.id, + 'create_date': order.create_date, + 'order_reference':order.order_id.id, + 'customer':order.order_partner_id.name, + 'product':order.product_id.name, + 'price_unit':_displayWithCurrency(local.get('lang_obj'), order.price_unit, + local.get('currencySymbol', ""), local.get('currencyPosition', "")), + 'quantity': order.product_uom_qty, + 'sub_total':_displayWithCurrency(local.get('lang_obj'), order.price_subtotal, + local.get('currencySymbol', ""), local.get('currencyPosition', "")), + 'delivered_qty':order.qty_delivered, + 'order_state':order.state, + 'marketplace_state':order.marketplace_state, + 'description': order.name, + } + result['sellerOrderLines'].append(temp) + else: + result = {'success': False, 'message': ('Marketplace is not Active !!!')} + else: + result = {'success': False, 'message': ('Customer is not a seller !!!')} + response.update(result) + return self._response('sellerOrderLines', response) + + @route(['/mobikul/marketplace/seller/orderline/'], csrf=False, type='http', auth="none", methods=['GET']) + def sellerOrderLinesDetail(self,line_id,**kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + PartnerObj = request.env['res.partner'].sudo() + Partner = PartnerObj.browse(response.get('customerId')) + if Partner and Partner.seller: + if response.get('addons', {}).get('odoo_marketplace'): + result = {'success': True} + local = response.get('local', {}) + SaleOrderLine = request.env['sale.order.line'].sudo() + domain = [('id', '=', line_id)] + orderline = SaleOrderLine.search(domain) + result['sellerOrderLineDetail'] = { + 'line_id': orderline.id, + 'create_date': orderline.create_date, + 'order_reference': orderline.order_id.id, + 'customer': orderline.order_partner_id.name, + 'product': orderline.product_id.name, + 'price_unit': _displayWithCurrency(local.get('lang_obj'), orderline.price_unit, + local.get('currencySymbol', ""), + local.get('currencyPosition', "")), + 'quantity': orderline.product_uom_qty, + 'sub_total': _displayWithCurrency(local.get('lang_obj'), orderline.price_subtotal, + local.get('currencySymbol', ""), + local.get('currencyPosition', "")), + 'delivered_qty': orderline.qty_delivered, + 'order_state': orderline.state, + 'marketplace_state': orderline.marketplace_state, + 'description': orderline.name, + 'order_payment_acquirer':orderline.order_payment_acquirer_id.id and orderline.order_payment_acquirer_id.id or "", + 'delivery_method':orderline.order_id.carrier_id and orderline.order_id.carrier_id.id or "" + + } + + else: + result = {'success': False, 'message': 'Marketplace is not Active !!!'} + else: + result = {'success': False, 'message': 'Customer is not a seller !!!'} + response.update(result) + return self._response('sellerOrderLinesDetail', response) + + # {"domain": "[('marketplace_seller_id','=',65),('status','=','approved')]", "offset": 0, "limit": 100} + + @route(['/mobikul/marketplace/seller/ask'], csrf=False, type='http', auth="none",methods=['POST']) + def sellerAsk(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + PartnerObj = request.env['res.partner'].sudo() + Partner = PartnerObj.browse(response.get('customerId')) + if Partner and Partner.seller: + ask_Query = "

%s

%s

"%(self._mData.get('title') or "",self._mData.get('body')) + mail_id = Partner.message_post(body=ask_Query, message_type='comment',subtype="mail.mt_comment",author_id=response.get('customerId')) + if mail_id: + result = {'message':_('Seller query is posted Successfully'),'success':True} + else: + result = {'message': 'Something went wrong in posted query', 'success': False} + else: + result = {'success': False, 'message': 'Customer is not a seller !!!'} + response.update(result) + return self._response('sellerAsk', response) + + @route(['/mobikul/marketplace/seller/product'], csrf=False, type='http', auth="none", methods=['POST']) + def sellerProduct(self, **kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success'): + PartnerObj = request.env['res.partner'].sudo() + Partner = PartnerObj.browse(response.get('customerId')) + TemplateObj = request.env['product.template'].sudo() + if Partner and Partner.seller: + local = response.get('local', {}) + domain = [('marketplace_seller_id', '=', response.get('customerId'))] + if self._mData.get('state'): + domain += [('status', '=', self._mData.get('state'))] + productDetailsCount = TemplateObj.search_count(domain) + productDetails = TemplateObj.search(domain,limit=self._mData.get('limit',response.get('itemsPerPage',5)), offset=self._mData.get('offset',0), order="id desc") + slr_product = [] + for prd in productDetails: + temp = { + "name": prd.name, + 'templateId':prd.id, + 'state': prd.status, + 'thumbNail' :_get_image_url(self.base_url, 'product.template', prd.id,'image'), + 'seller': prd.marketplace_seller_id.name, + 'qty': prd.qty, + 'priceUnit': _displayWithCurrency(local.get('lang_obj'),prd.list_price, local.get('currencySymbol',""), local.get('currencyPosition',"")), + } + slr_product.append(temp) + result= {'success': True,'sellerProduct':slr_product,"tcount":productDetailsCount,"offset":self._mData.get('offset',0)} + else: + result = {'success': False, 'message': 'Customer is not a seller !!!'} + response.update(result) + return self._response('sellerProduct', response) + + + @route(['/mobikul/marketplace/seller/dashboard'], csrf=False, type='http', auth="none",methods=['GET']) + def sellerDashboard(self, **kwargs): + Mobikul = request.env["mobikul"].sudo() + response = self._authenticate(True, **kwargs) + if response.get('success'): + PartnerObj = request.env['res.partner'].sudo() + Partner = PartnerObj.browse(response.get('customerId')) + if Partner and Partner.seller: + sellerData = Mobikul.sellerDashboardData(seller_Obj=Partner) + result = {'success': True,'sellerDashboard': sellerData} + else: + result = {'success': False, 'message': 'Customer is not a seller !!!'} + response.update(result) + return self._response('sellerDashboard', response) + + @route(['/mobikul/marketplace/seller/terms'], csrf=False, type='http', auth="none", methods=['GET']) + def sellerTermCond(self, **kwargs): + response = self._authenticate(False, **kwargs) + term_and_condition = request.env['website'].sudo().get_mp_config_settings_values().get('term_and_condition',False) + if response.get('success'): + result = { + "term_and_condition":term_and_condition and remove_htmltags(term_and_condition) or "", + } + response.update(result) + return self._response('sellerTermCond', response) + + + @route(['/mobikul/marketplace/become/seller'], csrf=False, type='http', auth="none", methods=['POST']) + def becomeSeller(self, **kwargs): + Mobikul = request.env["mobikul"].sudo() + UserObj = request.env['res.users'].sudo() + response = self._authenticate(True, **kwargs) + if response.get('success'): + user = UserObj.browse([response.get('userId')]) + if not user.partner_id.seller: + if self._mData.get('url_handler') and Mobikul.checkSellerUniqueUrl(self._mData.get('url_handler')): + Mobikul.set_marketplace_group_user(user) + user.partner_id.seller = True + user.partner_id.url_handler = self._mData.get('url_handler') + user.partner_id.country_id = self._mData.get('country_id') + result ={'success': True, 'message': 'Successfully became a seller'} + else: + result ={'success': False, 'message': _("Seller profile 'url_handler' is not unique or absent.")} + else: + result ={'success': False, 'message': 'Customer is already a seller' } + response.update(result) + return self._response('becomeSeller', response) + + + @route(['/mobikul/ShippingMethods'], csrf=False, type='http', auth="none", methods=['GET']) + def getAvailableShippingMethods(self,**kwargs): + response = self._authenticate(True, **kwargs) + if response.get('success') and response.get('addons', {}).get('website_sale_delivery'): + PartnerObj = request.env['res.partner'].sudo() + Partner = PartnerObj.browse(response.get('customerId')) + SaleOrder = Partner.last_website_so_id + ShippingMethods = SaleOrder.sudo()._get_delivery_methods() + if ShippingMethods: + local = response.get('local', {}) + result = [] + for method in ShippingMethods: + result.append({ + "name": method.name, + "id": method.id, + "description": method.website_description or "", + "price":_displayWithCurrency(local.get('lang_obj'), method.rate_shipment(SaleOrder).get('price'), + local.get('currencySymbol', ""), local.get('currencyPosition', "")), + }) + result = {'ShippingMethods':result} + else: + result = {'success':False, 'message':'No Active Shipping methods found.'} + else: + result = {'success':False, 'message':'Website Sale Delivery is not install.'} + response.update(result) + return self._response('getAvailableShippingMethods', response) diff --git a/ext/3rd-party-addons/mobikul/data/demo_data_view.xml b/ext/3rd-party-addons/mobikul/data/demo_data_view.xml new file mode 100755 index 00000000..cf1ada16 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/data/demo_data_view.xml @@ -0,0 +1,70 @@ + + + + + + Banner1 + product + 16 + + + + + Banner2 + none + + + + + signupNotify + + signup + Congrats...you have successfully created your account. + Feel free to contact us in case of any doubts. + none + + + + OrderNotify + + orderplaced + Thank you for your purchase ! + Your Order has been placed, successfully. + none + + + + Discount for You + automatic + new + default + 5 + 5 + center + + + Deal of the day + manual + + + + + + + fixed + 5 + 5 + center + + + New + automatic + wCategory + 13 + fixed + 5 + 5 + center + + + diff --git a/ext/3rd-party-addons/mobikul/data/mobikul_data.xml b/ext/3rd-party-addons/mobikul/data/mobikul_data.xml new file mode 100755 index 00000000..06066797 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/data/mobikul_data.xml @@ -0,0 +1,45 @@ + + + + + + Mobikul Sales + + + DEFAULT + + + + Mobikul App + + + + + + Automated mobikul banner Scheduler + + 1 + days + -1 + + + model.process_inactive_mobikul_banner() + + + + Sync Category + + + action=model.sync_category() + + + + + + + diff --git a/ext/3rd-party-addons/mobikul/i18n/fr.po b/ext/3rd-party-addons/mobikul/i18n/fr.po new file mode 100755 index 00000000..ff5c0ce3 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/i18n/fr.po @@ -0,0 +1,2356 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mobikul +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-04-18 10:27+0000\n" +"PO-Revision-Date: 2018-04-18 10:27+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: mobikul +#: code:addons/mobikul/controllers/main.py:770 +#, python-format +msgid " was removed from your Shopping Bag." +msgstr " was removed from your Shopping Bag." + +#. module: mobikul +#: model:ir.model.fields,field_description:mobikul.field_mobikul_product_slider_product_count +msgid "# Products" +msgstr "# Products" + +#. module: mobikul +#: code:addons/mobikul/models/mobikul.py:1123 +#, python-format +msgid "%s(copy)" +msgstr "%s(copy)" + +#. module: mobikul +#: model:ir.ui.view,arch_db:mobikul.mobikul_app_config_settings +msgid "(A paid feature)" +msgstr "(A paid feature)" + +#. module: mobikul +#: model:ir.ui.view,arch_db:mobikul.mobikul_sync_cat_form +msgid "Sync category with Website category" +msgstr "Sync category with Website category" + +#. module: mobikul +#: model:ir.ui.view,arch_db:mobikul.mobikul_product_template_form_view +msgid "\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: mobikul +#: code:addons/mobikul/controllers/main.py:770 +#, python-format +msgid " was removed from your Shopping Bag." +msgstr " was removed from your Shopping Bag." + +#. module: mobikul +#: model:ir.model.fields,field_description:mobikul.field_mobikul_product_slider_product_count +msgid "# Products" +msgstr "# Products" + +#. module: mobikul +#: code:addons/mobikul/models/mobikul.py:1123 +#, python-format +msgid "%s(copy)" +msgstr "%s(copy)" + +#. module: mobikul +#: model:ir.ui.view,arch_db:mobikul.mobikul_app_config_settings +msgid "(A paid feature)" +msgstr "(A paid feature)" + +#. module: mobikul +#: model:ir.ui.view,arch_db:mobikul.mobikul_sync_cat_form +msgid "Sync category with Website category" +msgstr "Sync category with Website category" + +#. module: mobikul +#: model:ir.ui.view,arch_db:mobikul.mobikul_product_template_form_view +msgid ") +# +########################################################################## +from . import mobikul +from . import product +from . import sale_order +from . import res_config +from . import ir_http diff --git a/ext/3rd-party-addons/mobikul/models/delivery.py b/ext/3rd-party-addons/mobikul/models/delivery.py new file mode 100755 index 00000000..977f45c3 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/models/delivery.py @@ -0,0 +1,13 @@ +# from odoo import fields,api, models +# +# +# class DeliveryCarrier(models.Model): +# _inherit = 'delivery.carrier' +# +# @api.multi +# def mobikul_publish_button(self): +# self.ensure_one() +# self.is_mobikul_available = not self.is_mobikul_available +# return True +# +# is_mobikul_available = fields.Boolean(default=False) diff --git a/ext/3rd-party-addons/mobikul/models/fcmAPI.py b/ext/3rd-party-addons/mobikul/models/fcmAPI.py new file mode 100755 index 00000000..32a7c8d1 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/models/fcmAPI.py @@ -0,0 +1,93 @@ +import requests +import json + +FCM_URL = "https://fcm.googleapis.com/fcm/send" + +class FCMAPI(object): + + def __init__(self, api_key): + self._API_KEY = api_key + + def _headers(self): + return { + "Content-Type": "application/json", + "Authorization": "Key=" + self._API_KEY + } + + def jsonDumps(self, data): + return json.dumps(data).encode('utf8') + + def _push(self, payload): + response = requests.post(FCM_URL, headers=self._headers(), data=payload) + print (response) + if response.status_code == 200: + if int(response.headers.get('content-length',0)) <= 0: + return {} + return response.json() + elif response.status_code == 401: + raise Exception("There was an error authenticating the sender account") + elif response.status_code == 400: + raise Exception(response.text) + else: + raise Exception("FCM server is temporarily unavailable") + + def send(self, payloads=None): + self.all_responses = [] + for payload in payloads: + response = self._push(payload) + self.all_responses.append(response) + return self.all_responses + + def parse_payload(self, registration_ids=None, topic_name=None, message_body=None, message_title=None, + message_icon=None, priority=False, data_message=None, badge=None, color=None, tag=None, + **extra_kwargs): + fcm_payload = dict() + if registration_ids: + if len(registration_ids) > 1: + fcm_payload['registration_ids'] = registration_ids + else: + fcm_payload['to'] = registration_ids[0] + if topic_name: + fcm_payload['to'] = '/topics/%s' % topic_name + if priority: + fcm_payload['priority'] = 'normal' + else: + fcm_payload['priority'] = 'high' + + if data_message: + if isinstance(data_message, dict): + fcm_payload['data'] = data_message + else: + raise Exception("Provided data_message is in the wrong format") + + fcm_payload['notification'] = {} + if message_icon: + fcm_payload['notification']['icon'] = message_icon + if message_body: + fcm_payload['notification']['body'] = message_body + if message_title: + fcm_payload['notification']['title'] = message_title + + if badge: + fcm_payload['notification']['badge'] = badge + if color: + fcm_payload['notification']['color'] = color + if tag: + fcm_payload['notification']['tag'] = tag + + if extra_kwargs: + fcm_payload['notification'].update(extra_kwargs) + + return self.jsonDumps(fcm_payload) + +if __name__== "__main__": + key = "AAAAkdP3f44:APA91bEyls0NapnpJi9IZvMe7jEpkYYx99bdCcFUYtIy4ZDEsEaKaZd1DRhodK6yIXiiWvRfwCE_17HU2uz04s--1qYSw635BgB_ilMxwlQEbBPOqoJkFTPpWnERbbJ2401Eyh27Szyv" + push_service = FCMAPI(api_key=key) + + registration_id = ["eSUQAef0S2Y:APA91bHm_Q2YJnzBJI77g7mTAW1teFd3i0t8gOM8HqJuv2RgO3_FFSI03ffShGCXus6jNNyILZ3Xng2jNKQdMCJ5FBPneJifkhQ9RJsC52reHfoXOxeEQvLFRbXZw1mKccUIGjf7P7_l"] + message_title = "Test" + message_body = "Hi Mohit, your customized news for today is ready" + payload = push_service.parse_payload(registration_ids=registration_id, message_title=message_title, message_body=message_body) + print (payload) + print (push_service.send([payload])) + diff --git a/ext/3rd-party-addons/mobikul/models/ir_http.py b/ext/3rd-party-addons/mobikul/models/ir_http.py new file mode 100755 index 00000000..edeba420 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/models/ir_http.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +########################################################################## +# +# Copyright (c) 2015-Present Webkul Software Pvt. Ltd. () +# +########################################################################## + +from odoo import api, models +from odoo import SUPERUSER_ID +from odoo.http import request + +class Http(models.AbstractModel): + _inherit = 'ir.http' + + rerouting_limit = 10 + _geoip_resolver = None + + @classmethod + def binary_content(cls, xmlid=None, model='ir.attachment', id=None, field='datas', + unique=False, filename=None,filename_field='datas_fname', download=False, + mimetype=None, default_mimetype='application/octet-stream', + access_token=None,env=None): + env = env or request.env + obj = None + if xmlid: + obj = env.ref(xmlid, False) + elif id and model in env: + obj = env[model].browse(int(id)) + if obj and 'is_mobikul_available' in obj._fields: + if env[obj._name].sudo().search([('id', '=', obj.id), ('is_mobikul_available', '=', True)]): + env = env(user=SUPERUSER_ID) + if obj._name == "res.partner" and field in ("image","profile_banner","profile_image"): + env = env(user=SUPERUSER_ID) + return super(Http, cls).binary_content( + xmlid=xmlid, model=model, id=id, field=field, unique=unique, filename=filename, + filename_field=filename_field, download=download, mimetype=mimetype, + default_mimetype=default_mimetype,access_token=access_token, env=env) diff --git a/ext/3rd-party-addons/mobikul/models/mobikul.py b/ext/3rd-party-addons/mobikul/models/mobikul.py new file mode 100755 index 00000000..fabd476e --- /dev/null +++ b/ext/3rd-party-addons/mobikul/models/mobikul.py @@ -0,0 +1,1352 @@ +# -*- coding: utf-8 -*- +########################################################################## +# +# Copyright (c) 2015-Present Webkul Software Pvt. Ltd. () +# +########################################################################## +from ast import literal_eval +from odoo import api, fields, models, _, SUPERUSER_ID +from odoo.tools.translate import html_translate +# import datetime +from odoo.exceptions import UserError +import random +import string +import inspect +import json +from .fcmAPI import FCMAPI +from datetime import datetime, timedelta +import logging +_logger = logging.getLogger(__name__) +from odoo.addons.base.ir.ir_mail_server import MailDeliveryException + +def _displayWithCurrency(lang_obj, amount, symbol, position): + fmt = "%.{0}f".format(2) # currency.decimal_places + # lang = cls.env['res.lang']._lang_get(cls.env.context.get('lang') or 'en_US') + + formatted_amount = lang_obj.format(fmt, amount , grouping=True, monetary=True) # currency.round(amount) + # _logger.info("-------%s"%formatted_amount) + return "%s%s"%(symbol,formatted_amount) if position=="before" else "%s%s"%(formatted_amount,symbol) + + +@api.model +def _lang_get(cls): + return cls.env['res.lang'].get_installed() + +def _default_unique_key(size, chars=string.ascii_uppercase + string.digits): + return ''.join(random.choice(chars) for x in range(size)) + +def _get_image_url(base_url, model_name, record_id, field_name, width=0, height=0): + """ Returns a local url that points to the image field of a given browse record. """ + if base_url and not base_url.endswith("/"): + base_url = base_url+"/" + if width or height: + return '%sweb/image/%s/%s/%s/%sx%s'% (base_url, model_name, record_id, field_name, width, height) + else: + return '%sweb/image/%s/%s/%s'% (base_url, model_name, record_id, field_name) + +def _getProductData(p_data, base_url, currency_symbol, currency_position, lang_obj): + result = [] + for prod in p_data: + result.append({ + 'templateId' :prod['id'] or '', + 'name' :prod['name'] or '', + 'priceUnit' :_displayWithCurrency(lang_obj, prod['lst_price'] or 0, currency_symbol, currency_position), + 'priceReduce' :prod['price'] < prod['lst_price'] and _displayWithCurrency(lang_obj, prod['price'] or 0, currency_symbol, currency_position) or "", + 'productId' :prod['product_variant_id'] and prod['product_variant_id'][0] or '', + 'productCount' :prod['product_variant_count'] or 0, + 'description' :prod['description_sale'] or '', + 'thumbNail' :_get_image_url(base_url, 'product.template', prod['id'],'image') + }) + return result + +def _get_product_fields(): + return ['name','product_variant_id','product_variant_count','price','description_sale','lst_price','website_price'] + +def _get_product_domain(): + return [("sale_ok", "=", True), ("is_mobikul_available", "=", True)] + +# def authenticate_request(fn): +# def ret_fn(*args, **kwargs): +# # userdata = inspect.getargspec(fn) +# # result = args[0].authenticate(args[1].get('usr'),args[1].get('pwd')) +# return "result %r %r"%(str(args), str(kwargs)) +# if not result['success']: +# return "result %r %r "%(args, kwargs) +# else: +# return fn(*args,**kwargs) +# return ret_fn + + +class Mobikul(models.Model): + _name = "mobikul" + _description = "Mobikul Model" + + @api.model + def _create_so(self, partner, local): + local = local or {} + result = {"success":True} + addr = partner.address_get(['delivery']) + so_data = { + 'partner_id': partner.id, + 'pricelist_id': self._context.get('pricelist'), + 'payment_term_id': partner.property_payment_term_id.id, + 'team_id': local.get("teamId"), + 'partner_invoice_id': partner.id, + 'partner_shipping_id': addr['delivery'], + 'user_id': local.get("salespersonId"), + } + company = self.env['product.pricelist'].browse(self._context.get('pricelist')).sudo().company_id + if company: + so_data['company_id'] = company.id + result['order'] = self.env['sale.order'].sudo().create(so_data) + + partner.write({'last_website_so_id': result['order'].id}) + result['cartId']=result['order'].id + + return result + + @api.model + def _create_so_line(self, order, Product, qty=1): + result = {"success":True} + SaleOrderLineSudo = self.env['sale.order.line'].sudo() + + product_context = dict(self.env.context) + product_context.setdefault('lang', order.partner_id.lang) + product_context.update({ + 'partner': order.partner_id.id, + 'quantity': qty, + 'date': order.date_order, + 'pricelist': order.pricelist_id.id, + }) + product = Product.with_context(product_context) + + so_line_data = { + 'name':product.name, + 'product_id': product.id, + 'product_uom_qty': qty, + 'order_id': order.id, + 'product_uom': product.uom_id.id, + 'price_unit': product.price, + } + order_line = SaleOrderLineSudo.create(so_line_data) + order_line.product_id_change() + order_line.product_uom_change() + order_line._onchange_discount() + return result + + + # @api.multi + # def _get_line_description(self, order, product_id, attributes=None): + # if not attributes: + # attributes = {} + + # product = self.env['product.product'].with_context(product_context).browse(product_id) + + # name = product.display_name + + # # add untracked attributes in the name + # untracked_attributes = [] + # for k, v in attributes.items(): + # # attribute should be like 'attribute-48-1' where 48 is the product_id, 1 is the attribute_id and v is the attribute value + # attribute_value = self.env['product.attribute.value'].sudo().browse(int(v)) + # if attribute_value and not attribute_value.attribute_id.create_variant: + # untracked_attributes.append(attribute_value.name) + # if untracked_attributes: + # name += '\n%s' % (', '.join(untracked_attributes)) + + # if product.description_sale: + # name += '\n%s' % (product.description_sale) + + # return name + + @api.model + def _validate(self, api_key,context=None): + context = context or {} + response = {'success':False, 'responseCode':0, 'message':_('Unknown Error !!!')} + if not api_key: + response['message'] = _('Invalid/Missing Api Key !!!') + return response + try: + # Get Mobikul Conf + mobikul = self.env['mobikul'].sudo().search([], limit=1) + if not mobikul: + response['responseCode'] = 1 + response['message'] = _("Mobikul Configuration not found !!!") + elif mobikul.api_key != api_key: + response['responseCode'] = 1 + response['message'] = _("API Key is invalid !!!") + else: + response['success'] = True + response['responseCode'] = 2 + response['message'] = _('Login successfully.') + response['lang'] = context.get('lang') or mobikul.default_lang and mobikul.default_lang.code or "en_US" + response['itemsPerPage'] = mobikul.product_limit + response['pricelist'] = mobikul.pricelist_id and mobikul.pricelist_id.id or 1 + response['local'] = { + 'currencySymbol':mobikul.pricelist_id and mobikul.pricelist_id.currency_id and mobikul.pricelist_id.currency_id.symbol or "", + 'currencyPosition':mobikul.pricelist_id and mobikul.pricelist_id.currency_id and mobikul.pricelist_id.currency_id.position or "", + 'teamId':mobikul.salesteam_id and mobikul.salesteam_id.id or False, + 'salespersonId':mobikul.salesperson_id and mobikul.salesperson_id.id or False, + 'lang_obj':self.env['res.lang']._lang_get(response['lang']) + } + response['addons'] = self.check_mobikul_addons() + except Exception as e: + response['responseCode'] = 3 + response['message'] = _("Login Failed:")+"%r"%e.message or e.name + return response + + @api.model + def _get_image_url(self, model_name, record_id, field_name, width=0, height=0, context=None): + """ Returns a local url that points to the image field of a given browse record. """ + context = context or {} + if context.get('base_url',"") and not context['base_url'].endswith("/"): + context['base_url'] = context['base_url'] + "/" + if width or height: + return '%sweb/image/%s/%s/%s/%sx%s'% (context.get('base_url'), model_name, record_id, field_name, width, height) + else: + return '%sweb/image/%s/%s/%s'% (context.get('base_url'), model_name, record_id, field_name) + + @api.model + def _get_cat_info(self, categ_obj, context=None): + context = context or {} + cat_data = { + "category_id":categ_obj.id, + "name":categ_obj.name or "", + "children":[], + "banner":self._get_image_url('mobikul.category',categ_obj.id,'banner', context=context), + "icon":self._get_image_url('mobikul.category',categ_obj.id,'icon', context=context), + } + return cat_data + + @api.model + def _recursive_cats(self, categ_obj, context=None): + context = context or {} + data = self._get_cat_info(categ_obj, context) + if categ_obj.child_id: + for cat_child in categ_obj.child_id: + data['children'].append(self._recursive_cats(cat_child, context)) + return data + + @api.model + def fetch_categories(self, context=None): + context = context or {} + all_cats = [] + if context.get('website_category',False): + return all_cats + else: + cat_obj = self.env['mobikul.category'].sudo() + top_cats = cat_obj.search([('parent_id','=',False)]) + for top_cat in top_cats: + all_cats.append(self._recursive_cats(top_cat, context)) + return all_cats + + @api.model + def fetch_featured_categories(self, context=None): + context = context or {} + all_fcats = [] + if context.get('website_category',False): + return all_fcats + else: + cat_obj = self.env['mobikul.category'].sudo() + f_cats = cat_obj.search([('type','=','featured')]) + + for f_cat in f_cats: + temp_f = { + 'categoryName':f_cat.name or "", + 'categoryId':f_cat.id + } + temp_f['url'] = self._get_image_url('mobikul.category',f_cat.id,'icon', context=context) + all_fcats.append(temp_f) + return all_fcats + + @api.model + def fetch_product_sliders(self,context=None): + context = context or {} + allProductSliders = [] + pSlider_obj = self.env['mobikul.product.slider'].sudo() + p_sliders = pSlider_obj.search([]) + for p_slider in p_sliders: + products = p_slider.get_product_data(context)['products'] + if not len(products): + continue + temp_s = { + 'title':p_slider.name or "", + 'item_display_limit':p_slider.item_display_limit, + 'slider_mode':p_slider.slider_mode or "", + 'product_img_position':p_slider.product_img_position or "", + 'products':products, + 'url':"/mobikul/sliderProducts/%d"%p_slider.id, + # 'color':p_slider.bk_color or "" + } + if p_slider.display_banner: + temp_s['backImage'] = self._get_image_url('mobikul.product.slider',p_slider.id,'banner', context=context) + allProductSliders.append(temp_s) + return allProductSliders + + @api.model + def fetch_user_info(self, user_obj, context=None): + context = context or {} + temp_i = { + 'customerBannerImage': self._get_image_url('res.partner',user_obj.partner_id.id,'banner_image', context=context), + 'customerProfileImage': self._get_image_url('res.partner',user_obj.partner_id.id,'image', context=context), + 'cartId': user_obj.partner_id.last_website_so_id and user_obj.partner_id.last_website_so_id.id or '', + # 'cartCount': user_obj.partner_id.last_website_so_id and user_obj.partner_id.last_website_so_id.cart_count or 0, + 'themeCode': '?', + 'customerName': user_obj.partner_id.name or "", + 'customerEmail': user_obj.login or "", + 'customerLang': user_obj.partner_id.lang.split('_')[0], + } + # if context.has_key('website') and context['website']: + # temp_i['cartId'] = user_obj.partner_id.last_website_so_id and user_obj.partner_id.last_website_so_id.id or '' + # temp_i['cartCount'] = user_obj.partner_id.last_website_so_id and user_obj.partner_id.last_website_so_id.cart_count or 0 + # else: + # temp_i['cartId'] = user_obj.partner_id.last_mobikul_so_id and user_obj.partner_id.last_mobikul_so_id.id or '' + # temp_i['cartCount'] = user_obj.partner_id.last_mobikul_so_id and user_obj.partner_id.last_mobikul_so_id.cart_count or 0 + return temp_i + + @api.model + def authenticate(self, credentials, detailed = False, isSocialLogin=False, context=None): + context = context or {} + response = {'success':False, 'responseCode':0, 'message':_('Unknown Error !!!')} + user = False + if not isinstance(credentials, dict): + response['message'] = _('Data is not in Dictionary format !!!') + return response + if isSocialLogin: + if not all(k in credentials for k in ('authProvider','authUserId')): + response['message'] = _('Insufficient data to authenticate !!!') + return response + provider = self._getAuthProvider(credentials['authProvider']) + try: + user = self.env['res.users'].sudo().search([('oauth_uid', '=', credentials['authUserId']),('oauth_provider_id', '=', provider)]) + if not user: + response['responseCode'] = 1 + response['message'] = _("Social-Login: No such record found.") + except Exception as e: + response['responseCode'] = 3 + response['message'] = _("Social-Login Failed.") + response['details'] = "%r"%e + else: + if not all(k in credentials for k in ('login','pwd')): + response['message'] = _('Insufficient data to authenticate !!!') + return response + try: + user = self.env['res.users'].sudo().search([('login', '=', credentials['login'])]) + if user: + user.sudo(user.id).check_credentials(credentials['pwd']) + else: + response['responseCode'] = 1 + response['message'] = _("Invalid email address.") + except Exception as e: + user = False + response['responseCode'] = 3 + response['message'] = _("Login Failed.") + response['details'] = "%r"%e + if user: + try: + response['success'] = True + response['responseCode'] = 2 + response['customerId'] = user.partner_id.id + response['userId'] = user.id + response['cartCount'] = user.partner_id.last_website_so_id and user.partner_id.last_website_so_id.cart_count or 0 + response['message'] = _('Login successfully.') + if self.check_mobikul_addons().get('website_sale_wishlist'): + response['WishlistCount'] = len(user.partner_id.wishlist_ids) + if self.check_mobikul_addons().get('email_verification'): + response['is_email_verified'] = user.wk_token_verified + if self.check_mobikul_addons().get('odoo_marketplace'): + response['is_seller'] = user.partner_id.seller + if user.partner_id.seller: + response['seller_state'] = self.check_seller_state(user) + if detailed: + response.update(self.fetch_user_info(user, context=context)) + except Exception as e: + response['responseCode'] = 3 + response['message'] = _("Login Failed.") + response['details'] = "%r"%e + return response + + def check_seller_state(self,user): + user_groupObj = self.env['ir.model.data'].sudo() + user_group_ids = user.groups_id.ids + xml_ids = ["marketplace_seller_group","marketplace_officer_group","marketplace_manager_group"] + mp_state = "pending" + for xml_id in xml_ids: + if user_groupObj.get_object_reference('odoo_marketplace', xml_id)[1] in user_group_ids: + mp_state = "approved" + break + return mp_state + + + def check_mobikul_addons(self): + result = {} + ir_model_obj = self.env['ir.module.module'].sudo() + result['website_sale_wishlist'] = ir_model_obj.search([('state', '=', 'installed'),('name', '=', 'website_sale_wishlist')]) and True or False + result['review'] = ir_model_obj.search([('state', '=', 'installed'),('name', '=', 'wk_review')]) and True or False + result['email_verification'] = ir_model_obj.search([('state', '=', 'installed'),('name', '=', 'email_verification')]) and True or False + result['odoo_marketplace'] = ir_model_obj.search([('state', '=', 'installed'),('name', '=', 'odoo_marketplace')]) and True or False + result['website_sale_delivery'] = ir_model_obj.search([('state', '=', 'installed'),('name', '=', 'website_sale_delivery')]) and True or False + return result + + def email_verification_defaults(self): + return self.env['email.verification.config'].sudo().get_values() + + def review_defaults(self): + return self.env['website.review.config'].sudo().get_values() + + @api.model + def homePage(self, cust_data, context): + context = context or {} + response = {} + + # Get base url + # if not context.has_key("base_url"): + if not 'base_url' in context: + + context['base_url'] = self.env['ir.config_parameter'].get_param('web.base.url') + + # Get Mobikul Conf + mobikul = self.env['mobikul'].sudo().search([], limit=1) + + # Get all Categories + response['categories'] = self.fetch_categories(context=context) + + # Get all Banners + response['bannerImages'] = self.fetch_banners(context=context) + + # Get all Featured categories + response['featuredCategories'] = self.fetch_featured_categories(context=context) + + #Get all Product Sliders + response['productSliders'] = self.fetch_product_sliders(context=context) + + return response + + + @api.model + def fetch_banners(self, context=None): + context = context or {} + all_banners = [] + MobBanners = self.env['mobikul.banners'].sudo().search_read([]) + for banner in MobBanners: + temp_d = { + "bannerName": banner['name'], + "bannerType": banner['banner_action'], + "id": '', + } + if banner['banner_action']=="product": + temp_d['id'] = banner["product_id"][0] + elif banner['banner_action']=="category": + temp_d['id'] = banner["category_id"][0] + elif banner['banner_action']=="custom": + temp_d['domain'] = "[('id','in',%r)]"%banner["product_ids"] + if banner['image']: + temp_d['url'] = self._get_image_url('mobikul.banners',banner['id'],'image', context=context) + else: + temp_d['url'] = banner['url'] + all_banners.append(temp_d) + # else: + # response['message'] = "No active banners found !!!" + # response['error_message'] = "Create some Banners, re-verify their publishing date." + return all_banners + + @api.model + def resetPassword(self, login): + response = {'success':False} + # MobikulConfigParam = self.search_read([], limit=1)[0] + # if MobikulConfigParam['reset_password_enabled']: + try: + if login: + self.env['res.users'].sudo().reset_password(login) + response['success'] = True + response['message'] = _("An email has been sent with credentials to reset your password") + else: + response['message'] = _("No login provided.") + except Exception as e: + # response['message'] = _("There is some error occurred. Please try again after some time.") + response['message'] = _("Invalid Username/Email.") + # else: + # response['message'] = "Error: Permission not granted !!!" + return response + + @api.model + def _getAuthProvider(self, provider): + if provider=="GMAIL": + google_provider = self.env.ref('auth_oauth.provider_google') + return google_provider and google_provider.id or False + elif provider=="FACEBOOK": + facebook_provider = self.env.ref('auth_oauth.provider_facebook') + return facebook_provider and facebook_provider.id or False + elif provider=="TWITTER": + twitter_provider = self.env.ref('auth_oauth.provider_twitter') + return twitter_provider and twitter_provider.id or False + return False + + @api.model + def _doSignup(self, data): + template_user_id = literal_eval(self.env['ir.config_parameter'].get_param('auth_signup.template_user_id', 'False')) + template_user = self.env['res.users'].browse(template_user_id) + if not template_user.exists(): return [False, 'Invalid template user'] + + values = { key: data.get(key) for key in ('login', 'name') } + values['email'] = data.get('email') or values.get('login') + # values['lang'] = self.lang + values['active'] = True + no_invitation_mail = True + if data.get('isSocialLogin',False): + values['oauth_uid'] = data.get('authUserId',"") + values['oauth_access_token'] = data.get('authToken',"") + values['oauth_provider_id'] = self._getAuthProvider(data.get('authProvider',"")) + values['email'] = values.get('email','provider_%s_user_%s' % (values['oauth_provider_id'], values['oauth_uid'])) + values['password'] = data.get("password",values['oauth_uid']) + if self.check_mobikul_addons().get('email_verification'): + values['wk_token_verified'] = True + if not values['oauth_uid'] or not values['oauth_provider_id'] or not values['name'] or not values['email']: + return [False,"Insufficient data to authenticate."] + else: + values['password'] = data.get('password',"") + no_invitation_mail = values['password'] and True or False + if not values['name'] or not values['email']: + return [False,"Insufficient data to authenticate."] + # if not all(values.itervalues()): return [False,"The form was not properly filled in."] + # if values.get('password') != data.get('confirm_password'): return [False, "Passwords do not match; please retype them."] + try: + with self.env.cr.savepoint(): + if self.check_mobikul_addons().get('odoo_marketplace') and data.get('is_seller'): + seller_msg = self.isMarketplaceSignup(template_user,values,data) + if seller_msg.get('status'): + return [True, seller_msg.get('user').id, seller_msg.get('user').partner_id.id,seller_msg.get('msg')] + else: + return [False,_("Seller profile 'url_handler' is not unique or absent.")] + else: + user = template_user.with_context(no_reset_password=no_invitation_mail).copy(values) + return [True, user.id, user.partner_id.id," "] + except Exception as e: + # copy may failed if asked login is not available. + # return [False,"Error: %r"%e] + return [False,_("There is some problem in creating account. Please try again later.")] + return [False, "Unknown Error"] + + def isMarketplaceSignup(self,template_user,values,data): + if data.get('url_handler') and self.checkSellerUniqueUrl(data.get('url_handler')): + user = template_user.with_context(no_reset_password=True).copy(values) + if user: + self.set_marketplace_group_user(user) + # user.partner_id.country = "India" + user.partner_id.seller = True + user.partner_id.url_handler = data.get('url_handler') + user.partner_id.country_id = data.get('country_id') + return {'status':True,'msg':_("Seller created successfully"),'user':user} + else: + {"status": False, "msg": _("Something went wrong please try again.")} + else: + return {"status": False, "msg": _("Seller profile 'url_handler' is not unique or absent.")} + + def set_marketplace_group_user(self,userObj): + user_group_id = self.env['ir.model.data'].get_object_reference('odoo_marketplace', 'marketplace_draft_seller_group')[1] + groups_obj = self.env["res.groups"].browse(user_group_id) + if groups_obj: + for group_obj in groups_obj: + group_obj.write({"users": [(4, userObj.id, 0)]}) + return True + else: + return False + + + + def checkSellerUniqueUrl(self,url): + if url: + check_url_existObj = self.env['res.partner'].sudo().search([('url_handler','=',url)],limit=1) + if len(check_url_existObj) == 0: + return True + else: + return False + else: + return False + + + @api.model + def signUp(self, form_data): + response = {'success':False} + # MobikulConfigParam = self.search_read([], limit=1)[0] + # if MobikulConfigParam['signup_enabled']: + + try: + if form_data.get('isSocialLogin',False): + provider = self._getAuthProvider(form_data['authProvider']) + user = self.env['res.users'].sudo().search([('oauth_uid', '=', form_data['authUserId']),('oauth_provider_id', '=', provider)]) + if user: + response['success'] = True + response['message'] = _("Login Successfully.") + response['userId'] = user.id + response['customerId'] = user.partner_id.id + return response + if 'login' in form_data: + if self.env['res.users'].sudo().search([("login", "=",form_data['login'])]): + # response['message'] = _("Another user is already registered using this email address.") + response['message'] = _("This mail is already registered with us. ") + + + else: + result = self._doSignup(form_data) + if result[0]: + response['success'] = True + # response['message'] = "An invitation has been sent to mentioned email, please accept it and set your password to complete the registration process." + # response['message'] = "A validation e-mail has been sent to your e-mail address." + response['message'] = _("Created Successfully.") + response['userId'] = result[1] + response['customerId'] = result[2] + response['seller_message'] =result[3] + else: + response['message'] = _("Could not create a new account: ")+"%s"%result[1] + else: + response['message'] = _("No login provided.") + except Exception as e: + response['message'] = _("Could not create a new account.:")+" %r"%e.message or e.name + # else: + # response['message'] = "Error: Permission not granted !!!" + return response + + @api.model + def getDefaultData(self): + IrConfigParam = self.env['ir.config_parameter'] + temp = {} + temp['allow_resetPwd'] = literal_eval(IrConfigParam.get_param('auth_signup.reset_password', 'False')) + temp['allow_signup'] = literal_eval(IrConfigParam.get_param('auth_signup.allow_uninvited', 'False')) + temp['allow_guestCheckout'] = literal_eval(IrConfigParam.get_param('mobikul.allow_guest', 'False')) + temp['allow_gmailSign'] = literal_eval(IrConfigParam.get_param('mobikul.gmail_signin', 'False')) + temp['allow_facebookSign'] = literal_eval(IrConfigParam.get_param('mobikul.facebook_signin', 'False')) + temp['allow_twitterSign'] = literal_eval(IrConfigParam.get_param('mobikul.twitter_signin', 'False')) + data = self.check_mobikul_addons() + #temp['allow_module_website_wishlist'] = data.get("wishlist") + temp['allowShipping'] = data.get("website_sale_delivery") + return temp + + def _default_order_mail_template(self): + return self.env.ref('sale.email_template_edi_sale').id + + def fetch_products(self, **kwargs): + """ + Extra Parameters: domain, limit, fields, offset, order + """ + domain = _get_product_domain() + result = {'offset':kwargs.get('offset',0)} + try: + if 'domain' in kwargs: + domain += literal_eval(kwargs['domain']) + except: + pass + if 'search' in kwargs: + for s in kwargs['search'].split(" "): + domain += [('name', 'ilike', s)] + if 'cid' in kwargs: + domain += [('mobikul_categ_ids', 'child_of', int(kwargs['cid']))] + + ProductObj = self.env['product.template'].sudo() + result['tcount'] = ProductObj.search_count(domain) + product_data = ProductObj.search_read(domain, limit=kwargs.get('limit',5), offset=result["offset"], order=kwargs.get('order',0), fields=_get_product_fields()) + + result['products'] = _getProductData(product_data, kwargs.get('base_url', self._get_base_url()), kwargs.get('currencySymbol'), kwargs.get('currencyPosition'), kwargs.get('lang_obj')) + return result + + def _get_base_url(self): + return self.env['ir.config_parameter'].get_param('web.base.url') + + def _default_language(self): + lc = self.env['ir.default'].get('res.partner', 'lang') + dl = self.env['res.lang'].search([('code', '=', lc)], limit=1) + return dl.id if dl else self.env['res.lang'].search([]).ids[0] + + def _active_languages(self): + return self.env['res.lang'].search([]).ids + + def add_to_cart(self,partner,product_id,set_qty,add_qty,response): + PartnerObj = self.env['res.partner'].sudo() + Partner = PartnerObj.browse(partner) + if Partner: + Product = self.env['product.product'].sudo().search([('id','=',product_id)]) + if Product: + last_order = Partner.last_website_so_id + if last_order: + flag = 0 + for line in last_order.order_line: + if line.product_id == Product: + if set_qty: + line.product_uom_qty = set_qty + line.product_uom_change() + line._onchange_discount() + flag = 1 + elif add_qty: + line.product_uom_qty+=int(add_qty) + line.product_uom_change() + line._onchange_discount() + flag = 1 + else: + flag = -1 + result = {'message':'Insufficient data.', 'success':False} + if not flag: + #Create order line + self._create_so_line(last_order, Product, int(add_qty)) + flag = True + if flag==1: + result = {'message':'Added Successfully.','productName':Product.display_name, 'success':True} + result['cartCount'] = last_order.cart_count + return result + + else: + #create Order + local = response.get('local',{}) + res = self._create_so(Partner, local) + self._create_so_line(res['order'], Product, int(add_qty)) + result = {'message':'Added Successfully.','cartCount':res['order'].cart_count,'productName':Product.display_name, 'success':True} + return result + else: + result = {'success':False, 'message':'Insufficient data.'} + return result + else: + result = {'success':False, 'message':'Account not found !!!'} + return result + + def sellerDashboardData(self,seller_Obj): + prdObj = self.env['product.template'].sudo() + SaleOrderLine = self.env['sale.order.line'].sudo() + approved_count = prdObj.search_count([('marketplace_seller_id', '=', seller_Obj.id), ('status', '=', 'approved')]) + pending_count = prdObj.search_count([('marketplace_seller_id', '=', seller_Obj.id), ('status', '=', 'pending')]) + rejected_count = prdObj.search_count([('marketplace_seller_id', '=', seller_Obj.id), ('status', '=', 'rejected')]) + new_sol_count = SaleOrderLine.search_count([('marketplace_seller_id', '=', seller_Obj.id), ('marketplace_state', '=', 'new')]) + approved_sol_count = SaleOrderLine.search_count([('marketplace_seller_id', '=', seller_Obj.id), ('marketplace_state', '=', 'approved')]) + shipped_sol_count = SaleOrderLine.search_count([('marketplace_seller_id', '=', seller_Obj.id), ('marketplace_state', '=', 'shipped')]) + temp = { + "approved_productCount":approved_count, + "pending_productCount":pending_count, + "rejected_productCount":rejected_count, + "new_solCount":new_sol_count, + "approved_solCount":approved_sol_count, + "shipped_solCount":shipped_sol_count, + "total":{ + "label":"Total Amount", + "value":seller_Obj.total_mp_payment + }, + "balance":{ + "label": "Balance Amount", + "value":seller_Obj.balance_mp_payment + }, + + } + + return temp + + + def easy_date(self,time=False): + """ + Get a datetime object or a timestamp and return a + easy read string like 'Just now', 'Yesterday', '3 months ago', + 'Year ago'. + """ + now = datetime.now() + if type(time) is str: + time = fields.Datetime.from_string(time) + if type(time) is int: + diff = now - datetime.fromtimestamp(time) + elif isinstance(time,datetime): + diff = now - time + elif not time: + diff = now - now + second_diff = diff.seconds + day_diff = diff.days + + if day_diff < 0: + return '' + + if day_diff == 0: + if second_diff < 10: + return "just now" + if second_diff < 60: + return str(second_diff) + " seconds ago" + if second_diff < 120: + return "a minute ago" + if second_diff < 3600: + return str(second_diff / 60) + " minutes ago" + if second_diff < 7200: + return "an hour ago" + if second_diff < 86400: + return str(second_diff / 3600) + " hours ago" + if day_diff == 1: + return "Yesterday" + if day_diff < 7: + return str(day_diff) + " days ago" + if day_diff < 31: + return str(day_diff / 7) + " weeks ago" + if day_diff < 365: + return str(day_diff / 30) + " months ago" + return str(day_diff / 365) + " years ago" + + def _getdefaultWebsite_id(self): + website_id = self.env['website'].search([], limit=1) + return website_id.id + + + name = fields.Char('Mobikul App Title', default="Mobikul App", required=1) + salesperson_id = fields.Many2one('res.users', string='Default Salesperson') + salesteam_id = fields.Many2one('crm.team', string='Default Sales Team') + api_key = fields.Char(string='API Secret key', default="dummySecretKey", required=1) + fcm_api_key = fields.Char(string='FCM Api key') + color_scheme = fields.Selection([ + ('default', 'Default'), + ('red-green', 'Red-Green'), + ('light-green', 'Light Green'), + ('deep-purple-pink', 'Deep Purple-Pink'), + ('blue-orange', 'Blue Orange'), + ('light-blue-red', 'Light Blue-Red')], + string='Color Scheme', required=True, + default='default', + help="Color Options for your Mobikul App.") + email_verify = fields.Boolean(default=True, string='Verify Email on signUp', help="A verification email will send to the user after signup to cross-check user`s identity.") + default_lang = fields.Many2one('res.lang', string='Default Language', default=_default_language, + help="If the selected language is loaded in the mobikul, all documents related to " + "this contact will be printed in this language. If not, it will be English.") + + language_ids = fields.Many2many('res.lang', 'mobikul_lang_rel', 'mobikul_id', 'lang_id', 'Languages', default=_active_languages) + pricelist_id = fields.Many2one('product.pricelist', string='Default Pricelist') + currency_id = fields.Many2one('res.currency', related='pricelist_id.currency_id', string='Default Currency', readonly=True) + order_mail_template = fields.Many2one('mail.template', string='Confirmation Email', readonly=True, default=_default_order_mail_template, help="Email sent to customer at the end of the checkout process") + product_limit = fields.Integer(default=10, string='Limit Products per page', help='Used in Pagination', required=1) + website_id = fields.Many2one('website', default=_getdefaultWebsite_id,help="select website id for the app") + + @api.multi + def unlink(self): + raise UserError(_('You cannot remove/deactivate this Configuration.')) + +class MobikulBanners(models.Model): + _name = 'mobikul.banners' + _description = 'Mobikul Banner Class' + _order = "sequence, name" + + name = fields.Char('Title', required=True, translate=True) + active = fields.Boolean(default=True) + description = fields.Text('Description', translate=True) + image = fields.Binary('Image', attachment=True) + banner_action = fields.Selection([ + ('product', 'Open Product Page'), + ('category', 'Open Category Page'), + ('custom', 'Open Custom Collection Page'), + ('none', 'Do nothing')], + string='Action to be triggered', required=True, + default='none', + help="Define what action will be triggerred when click/touch on the banner.") + product_id = fields.Many2one('product.template', string='Choose Product') + category_id = fields.Many2one('mobikul.category', string='Choose Category') + product_ids = fields.Many2many('product.template', string='Choose Products') + url = fields.Char('Image URL', help="Static URL of Banner Image, used when banner`s image is not present.") + date_published = fields.Datetime('Publish Date') + date_expired = fields.Datetime('Expiration Date') + sequence = fields.Integer(default=10, help='Display order') + total_views = fields.Integer('Total # Views', default=0, readonly=1) + + + @api.model + def create(self, values): + if not values.get('image') and not values.get('url'): + raise UserError(_('Please upload Banner`s Image or enter Image URL')) + if not values.get('date_published'): + values['date_published'] = datetime.now() + return super(MobikulBanners, self).create(values) + + + # crone for auto inactive expire banners + @api.model + def process_inactive_mobikul_banner(self): + banner = self.sudo().search([]) + inactive_MobBanners = [] + for b in banner: + if b.date_expired: + if fields.Date.from_string(fields.Datetime.now()) > fields.Date.from_string(b.date_expired): + inactive_MobBanners.append(b) + for i in inactive_MobBanners: + i.active = False + +# method to check expire banners + @api.model + def remove_expireBanners(self): + banner = self.sudo().search([]) + MobBanners = [] + for b in banner: + if b.date_expired: + if fields.Date.from_string(fields.Datetime.now()) <= fields.Date.from_string(b.date_expired): + MobBanners.append(b) + else: + MobBanners.append(b) + return MobBanners + +class MobikulCategory(models.Model): + _name = 'mobikul.category' + _description = 'Mobikul Category' + _order = "sequence, name" + + name = fields.Char(required=True, translate=True) + active = fields.Boolean(default=True) + parent_id = fields.Many2one('mobikul.category', string='Parent Category', index=True) + child_id = fields.One2many('mobikul.category', 'parent_id', string='Children Categories') + banner = fields.Binary('Banner', attachment=True) + icon = fields.Binary('Icon', attachment=True) + sequence = fields.Integer(default=10, help='Display order') + complete_name = fields.Char( + 'Complete Name', compute='_compute_complete_name', + store=True) + type = fields.Selection([ + ('featured', 'Featured Category'), + ('normal', 'Normal')], 'Category Type', default='normal', + help="A Featured category is a category that can be used ...") + #this field is added for mobikul category merge + website_cat_id = fields.Many2one('product.public.category', 'Webiste Category') + + @api.depends('name', 'parent_id.complete_name') + def _compute_complete_name(self): + for category in self: + if category.parent_id: + category.complete_name = '%s / %s' % (category.parent_id.complete_name, category.name) + else: + category.complete_name = category.name + + @api.constrains('parent_id') + def check_parent_id(self): + if not self._check_recursion(): + raise ValueError(_('Error ! You cannot create recursive categories.')) + + @api.multi + def name_get(self): + res = [] + for category in self: + names = [category.name] + parent_category = category.parent_id + while parent_category: + names.append(parent_category.name) + parent_category = parent_category.parent_id + res.append((category.id, ' / '.join(reversed(names)))) + return res + + # method call from server action + @api.model + def sync_category(self): + action = self.env.ref('mobikul.mobikul_sync_cat_action').read()[0] + action['views'] = [(self.env.ref('mobikul.mobikul_sync_cat_form').id, 'form')] + action['context'] = self._context + return action + + # def _compute_products(self): + # read_group_res = self.env['product.template'].read_group([('categ_id', 'in', self.ids)], ['categ_id'], ['categ_id']) + # group_data = dict((data['categ_id'][0], data['categ_id_count']) for data in read_group_res) + # for categ in self: + # categ.product_count = group_data.get(categ.id, 0) + +class MobikulSyncCategory(models.Model): + _name = "mobikul.sync.category" + + sync_type = fields.Selection([ + ('name', 'Only Name'), + ('name_seq', 'Name and Sequence'), + ('name_parent', 'Name and Parent Category'), + ('name_parent_seq', 'Name, Parent Category and Sequence'), ], 'Sync Type', default='name_parent_seq', + help="Sync mobikul category on the basis of Selection") + + def show_msg_wizard(self, msg): + partial_id = self.env['wk.wizard.message'].create({'text': msg}) + return { + 'name': "Message", + 'view_mode': 'form', + 'view_id': False, + 'view_type': 'form', + 'res_model': 'wk.wizard.message', + 'res_id': partial_id.id, + 'type': 'ir.actions.act_window', + 'nodestroy': True, + 'target': 'new', + } + + @api.multi + def sync_mobikul_cat_with_web_cat(self): + isCatMerge = self.env['ir.module.module'].sudo().search( + [('state', '=', 'installed'), ('name', '=', 'mobikul_cat_merge')]) and True or False + if isCatMerge: + mobikul_catg_ids = self.env['mobikul.category'].search([("id","in",self._context.get('active_ids'))]) + if not self.sync_type: + raise UserError(_("Please select the Sync Type.")) + for mob_cat in mobikul_catg_ids: + if mob_cat.website_cat_id.id: + if self.sync_type == "name": + mob_cat.name = mob_cat.website_cat_id.name + elif self.sync_type == "name_parent": + mob_cat.name = mob_cat.website_cat_id.name + mob_cat.parent_id = mob_cat.website_cat_id.parent_id.mobikul_cat_id.id + elif self.sync_type == "name_parent_seq": + mob_cat.name = mob_cat.website_cat_id.name + mob_cat.parent_id = mob_cat.website_cat_id.parent_id.mobikul_cat_id.id + mob_cat.sequence = mob_cat.website_cat_id.sequence + elif self.sync_type == "name_seq": + mob_cat.name = mob_cat.website_cat_id.name + mob_cat.sequence = mob_cat.website_cat_id.sequence + + else: + _logger.info("MESAGE :Website category is not present in Mobikul Caegory") + if self.sync_type == "name": + message = "Done! name are synced with mobikul category from Website category." + elif self.sync_type == "name_seq": + message = "Done! name and sequence are synced with mobikul category from Website category." + elif self.sync_type == "name_parent": + message = "Done! name and parent category are synced with mobikul categories from Website category." + elif self.sync_type == "name_parent_seq": + message = "Done! name, sequence and parent category are synced with mobikul categories from Website category." + else: + message = "Install the Mobikul Category Merge Module First" + return self.show_msg_wizard(message) + +class MobikulPushNotificationTemplate(models.Model): + _name = 'mobikul.push.notification.template' + _description = 'Mobikul Push Notification Templates' + _order = "name" + + def _addMe(self, data): + self.env["mobikul.notification.messages"].sudo().create(data) + return True + + + def _get_key(self): + mobikul = self.env['mobikul'].sudo().search([], limit=1) + return mobikul and mobikul.fcm_api_key or "" + + @api.model + def _pushMe(self,key, payload_data, data=False): + status = True + summary = "" + try: + push_service = FCMAPI(api_key=key) + summary = push_service.send([payload_data]) + if data: + self._addMe(data) + except Exception as e: + status = False + summary = "Error: %r"%e + return [status,summary] + + @api.model + def _send(self, to_data, customer_id=False, max_limit=20): + """ + to_data = dict(to or registration_ids) + """ + if type(to_data)!=dict: + return False + if not to_data.get("to",False) and not to_data.get("registration_ids",False): + if not customer_id: + return False + reg_data = self.env['fcm.registered.devices'].sudo().search_read([('customer_id','=',customer_id)],limit=max_limit, fields=['token']) + if not reg_data: + return False + to_data = { + "registration_ids":[r['token'] for r in reg_data] + } + notification = dict(title=self.notification_title, body=self.notification_body) + if self.notification_color: + notification['color'] = self.notification_color + if self.notification_tag: + notification['tag'] = self.notification_tag + + fcm_payload = dict(notification=notification) + fcm_payload.update(to_data) + data_message = dict(type="",id="",domain="",image="",name="") + + if self.banner_action == 'product': + data_message['type'] = 'product' + data_message['id'] = self.product_id.id + data_message['name'] = self.product_id.name + elif self.banner_action == 'category': + data_message['type'] = 'category' + data_message['id'] = self.category_id.id + data_message['name'] = self.category_id.name + elif self.banner_action == 'custom': + data_message['type'] = 'custom' + data_message['domain'] = "[('id','in',%s)]"%self.product_ids.ids + data_message['name'] = self.notification_title + else: + data_message['type'] = 'none' + data_message['image'] = _get_image_url(self._context.get('base_url'), 'mobikul.push.notification.template', self.id,'image') + data_message['notificationId'] = random.randint(1, 99999) + fcm_payload['data'] = data_message + if customer_id: + data = dict( + title=self.notification_title, body=self.notification_body, customer_id=customer_id, + banner=self.image, datatype='default' + ) + return self._pushMe(self._get_key(), json.dumps(fcm_payload).encode('utf8'), customer_id and data or False) + + name = fields.Char('Name', required=True, translate=True) + notification_color = fields.Char('Color',default='PURPLE') + notification_tag = fields.Char('Tag') + notification_title = fields.Char('Title', required=True, translate=True) + active = fields.Boolean(default=True, copy=False) + notification_body = fields.Text('Body', translate=True) + image = fields.Binary('Image', attachment=True) + banner_action = fields.Selection([ + ('product', 'Open Product Page'), + ('category', 'Open Category Page'), + ('custom', 'Open Custom Collection Page'), + ('none', 'Do nothing')], + string='Action', required=True, + default='none', + help="Define what action will be triggerred when click/touch on the banner.") + product_id = fields.Many2one('product.template', string='Choose Product') + product_ids = fields.Many2many('product.template', string='Choose Products') + category_id = fields.Many2one('mobikul.category', string='Choose Category') + device_id = fields.Many2one('fcm.registered.devices', string='Select Device') + total_views = fields.Integer('Total # Views', default=0, readonly=1, copy=False) + condition = fields.Selection([ + ('signup', 'Customer`s SignUp'), + ('orderplaced',"Order Placed") + ], string='Condition', required=True, default='signup') + + @api.multi + def dry_run(self): + self.ensure_one() + to_data = dict(to=self.device_id and self.device_id.token or "") + result = self._send(to_data, self.device_id and self.device_id.customer_id and self.device_id.customer_id.id or False) + # raise UserError('Result: %r'%result) + + @api.multi + def copy(self, default=None): + self.ensure_one() + default = dict(default or {}, name=_('%s(copy)') % self.name) + return super(MobikulPushNotificationTemplate, self).copy(default) + + +class MobikulPushNotification(models.Model): + _name = 'mobikul.push.notification' + _description = 'Mobikul Push Notification' + _order = "activation_date, name" + _inherit = ['mobikul.push.notification.template'] + + + @api.model + def parse_n_push(self, max_limit=20, registration_ids=None): + to_data = dict() + if self.notification_type == 'token-auto': + reg_data = self.env['fcm.registered.devices'].sudo().search_read(limit=max_limit, fields=['token']) + registration_ids = [r['token'] for r in reg_data] + elif self.notification_type == 'token-manual': + registration_ids = [d.token for d in self.device_ids] + elif self.notification_type == 'topic': + to_data['to'] = '/topics/%s' % self.topic_id.name + else: + return [False,"Insufficient Data"] + + if registration_ids: + if len(registration_ids) > 1: + to_data['registration_ids'] = registration_ids + else: + to_data['to'] = registration_ids[0] + return self._send(to_data) + + summary = fields.Text('Summary', readonly=True) + activation_date = fields.Datetime('Activation Date', copy=False) + notification_type = fields.Selection([ + ('token-auto', 'Token-Based(All Reg. Devices)'), + ('token-manual', 'Token-Based(Selected Devices)'), + ('topic', 'Topic-Based'), + ], + string='Type', required=True, + default='token-auto') + topic_id = fields.Many2one('fcm.registered.topics', string='Choose Topic') + device_ids = fields.Many2many('fcm.registered.devices', string='Choose Devices/Customers') + state = fields.Selection([ + ('draft', 'Draft'), + ('confirm', 'Confirm'), + ('hold', 'Hold'), + ('error', 'Error'), + ('done', 'Done'), + ('cancel', 'Cancelled'), + ], string='Status', readonly=True, copy=False, index=True, track_visibility='onchange', default='draft') + + @api.multi + def action_cancel(self): + for record in self: + record.state = 'cancel' + return True + + @api.multi + def action_confirm(self): + for record in self: + record.state = 'confirm' + return True + + @api.multi + def action_draft(self): + for record in self: + record.state = 'draft' + return True + + @api.multi + def action_hold(self): + for record in self: + record.state = 'hold' + return True + + @api.multi + def push_now(self): + for record in self: + response = record.parse_n_push() + record.state = response[0] and 'done' or 'error' + record.summary = response[1] + return True + + @api.multi + def duplicate_me(self): + self.ensure_one() + action = self.env.ref('mobikul.mobikul_push_notification_action').read()[0] + action['views'] = [(self.env.ref('mobikul.mobikul_push_notification_view_form').id, 'form')] + action['res_id'] = self.copy().id + return action + +class MobikulProductSlider(models.Model): + _name = 'mobikul.product.slider' + _description = 'Mobikul Product Slider' + _order = "sequence" + + @api.multi + def _products_count(self): + r = {} + for slider in self: + slider.product_count = r.get(slider.id, len(slider.get_product_data({'count':True}))) + return r + + @api.multi + def action_view_products(self): + products = self.get_product_data({'count':True}) + action = self.env.ref('mobikul.mobikul_product_template_action').read()[0] + action['domain'] = [('id', 'in', products)] + return action + + @api.multi + def get_product_data(self,context=None): + context=context or {} + if not 'base_url' in context: + context['base_url'] = self.env['ir.config_parameter'].get_param('web.base.url') + orderBy = context.get('order',None) + mlimit = context.get('limit',self.item_limit) + moffset = context.get('offset',0) + + prod_obj = self.env['product.template'].sudo() + product_filter = _get_product_domain() + if self.product_selection == "manual": + product_filter.append(('id','in',self.product_ids._ids)) + elif self.product_based_on == 'new': + orderBy = 'id desc' + elif self.product_based_on == 'iCategory': + product_filter.append(('categ_id','=',self.icategory_id and self.icategory_id.id or False)) + elif self.product_based_on == 'wCategory': + product_filter.append(('public_categ_ids','=',self.wcategory_id and self.wcategory_id.id or False)) + elif self.product_based_on == 'mCategory': + product_filter.append(('mobikul_categ_ids','=',self.mcategory_id and self.mcategory_id.id or False)) + all_prods = prod_obj.search(product_filter, order=orderBy) + if 'count' in context: + return all_prods._ids + else: + product_data = prod_obj.search_read(product_filter, limit=mlimit, fields=_get_product_fields(), offset=moffset, order=orderBy) + result = {'tcount':len(all_prods),'offset':moffset} + result['products'] = _getProductData(product_data, context.get('base_url'), context.get('currencySymbol'), context.get('currencyPosition'), context.get('lang_obj')) + return result + + name = fields.Char('Slider Title', required=True, translate=True) + active = fields.Boolean(default=True) + sequence = fields.Integer(default=10, help='Display order', required=True) + description = fields.Text('Description', translate=True) + display_banner = fields.Boolean('Display Banner', default=False) + banner = fields.Binary('Banner', attachment=True) + url = fields.Char('Link URL', help="Used when someone click on banner/view-all button.") + product_ids = fields.Many2many('product.template', string='Choose Products') + total_views = fields.Integer('Total # Views', default=0, readonly=1) + item_limit = fields.Integer('Maximum no. of products in a slider', default=5, required=True) + item_display_limit = fields.Integer('Display no. of Products in a slider(per row)', default=5, required=True) + product_img_position = fields.Selection([ + ('center', 'Center'), + ('left', 'Left'), + ('right', 'Right'), + ], + string='Product Image Position', required=True, + default='center') + slider_mode = fields.Selection([ + ('default', 'Default(Slide)'), + ('fixed', 'Fixed'), + ], + string='Slider Mode', required=True, + default='default', + help="Define which type of behaviour you want with your Slider.") + product_selection = fields.Selection([ + ('manual', 'Manual'), + ('automatic', 'Automatic'), + ], + string='Selection Criteria', required=True, default='automatic') + product_based_on = fields.Selection([ + ('new', 'Newly created'), + ('iCategory', 'Internal Category'), + ('mCategory', 'Mobikul Category'), + ('wCategory', 'Website Category'), + ], + string='Based on', default='new') + mcategory_id = fields.Many2one('mobikul.category', string='Mobikul Category') + wcategory_id = fields.Many2one('product.public.category', string='Website Category') + icategory_id = fields.Many2one('product.category', string='Internal Category') + product_count = fields.Integer(compute='_products_count', string='# Products') + bk_color = fields.Char('Background Color') + +class FcmRegisteredDevices(models.Model): + _name = 'fcm.registered.devices' + _description = 'All Registered Devices on FCM for Push Notifications.' + _order = 'write_date desc' + + @api.multi + def name_get(self): + res = [] + for record in self: + name = record.customer_id and record.customer_id.name or '' + res.append((record.id, "%s(DeviceId:%s)"%(name,record.device_id))) + return res + + name = fields.Char('Name') + token = fields.Text('FCM Registration ID', readonly=True) + device_id = fields.Char('Device Id', readonly=True) + customer_id = fields.Many2one('res.partner', string="Customer", readonly=True, index=True) + active = fields.Boolean(default=True, readonly=True) + # write_date = fields.Datetime(string='Last Update', readonly=True, help="Date on which this entry is created.") + description = fields.Text('Description', readonly=True) + +class FcmRegisteredTopics(models.Model): + _name = 'fcm.registered.topics' + _description = 'All Registered Topics for Push Notifications.' + + name = fields.Char('Topic Name', required=True) + + + +class MobikulNotificationMessages(models.Model): + _name = 'mobikul.notification.messages' + _description = 'Mobikul Notification Messages' + + name = fields.Char('Message Name') + title = fields.Char('Title') + subtitle = fields.Char('Subtitle') + body = fields.Text('Body') + icon = fields.Binary('Icon') + banner = fields.Binary('Banner') + is_read = fields.Boolean('Is Read',default=False, readonly=True) + customer_id = fields.Many2one('res.partner', string="Customer", index=True) + active = fields.Boolean(default=True, readonly=True) + period = fields.Char('Period',compute='_compute_period') + datatype = fields.Selection([ + ('default', 'Default'), + ('order','Order')], + string='Data Type', required=True, + default='default', + help="Notification Messages Data Type for your Mobikul App.") + + + def _compute_period(self): + for i in self: + i.period = self.env['mobikul'].easy_date(i.create_date) diff --git a/ext/3rd-party-addons/mobikul/models/product.py b/ext/3rd-party-addons/mobikul/models/product.py new file mode 100755 index 00000000..dc40509a --- /dev/null +++ b/ext/3rd-party-addons/mobikul/models/product.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +########################################################################## +# +# Copyright (c) 2015-Present Webkul Software Pvt. Ltd. () +# +########################################################################## +from odoo import api, fields, models, _ +import logging +_logger = logging.getLogger(__name__) + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + @api.multi + def mobikul_publish_button(self): + self.ensure_one() + self.is_mobikul_available = not self.is_mobikul_available + return True + + + mobikul_categ_ids = fields.Many2many('mobikul.category', string='Mobikul Product Category') + mobikul_status = fields.Selection([ + ('empty', 'Display Nothing'), + ('in_stock', 'In-Stock'), + ('out_stock', 'Out-of-Stock'), + ], "Product Availability", default='empty', help="Adds an availability status on the mobikul product page.") + is_mobikul_available = fields.Boolean("Published on App", default=1, help="Allow the end user to choose this price list") + + +class ProductPublicCategory(models.Model): + _inherit = 'product.public.category' + # this field is added for mobikul category merge + mobikul_cat_id = fields.Many2one('mobikul.category', 'Mobikul Category') + + + +class CrmTeam(models.Model): + _inherit = "crm.team" + + mobikul_ids = fields.One2many('mobikul', 'salesteam_id', string='Mobikul', help="Mobikul is using these sales team.") diff --git a/ext/3rd-party-addons/mobikul/models/res_config.py b/ext/3rd-party-addons/mobikul/models/res_config.py new file mode 100755 index 00000000..d7e2ebad --- /dev/null +++ b/ext/3rd-party-addons/mobikul/models/res_config.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +########################################################################## +# +# Copyright (c) 2015-Present Webkul Software Pvt. Ltd. () +# +########################################################################## +from odoo import api, fields, models +from odoo.tools.safe_eval import safe_eval +import logging +_logger = logging.getLogger(__name__) + + +class MobikulConfigSettings(models.TransientModel): + _name = 'mobikul.config.settings' + _inherit = 'res.config.settings' + + def _default_mobikul(self): + return self.env['mobikul'].search([], limit=1).id + + def _default_order_mail_template(self): + return self.env.ref('sale.email_template_edi_sale').id + + def open_mobikul_conf(self): + return { + 'type': 'ir.actions.act_window', + 'name': 'Mobikul-App Configuration', + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'mobikul', + 'res_id': self.mobikul_app.id, + 'target': 'current', + } + + # @api.multi + def open_default_user(self): + action = self.env.ref('base.action_res_users').read()[0] + action['context'] = self.env.context + action['res_id'] = self.env.ref('base.default_user').id + action['views'] = [[self.env.ref('base.view_users_form').id, 'form']] + return action + + mobikul_app = fields.Many2one('mobikul', string="Mobikul APP", default=_default_mobikul, required=True) + app_name = fields.Char('App Name', related='mobikul_app.name') + product_limit = fields.Integer('Limit Products per page', related='mobikul_app.product_limit') + module_email_verification = fields.Boolean(related='mobikul_app.email_verify', string='Verify Email on signUp') + salesperson_id = fields.Many2one('res.users', related='mobikul_app.salesperson_id', string='Salesperson') + salesteam_id = fields.Many2one('crm.team', related='mobikul_app.salesteam_id', string='Sales Team') + default_lang = fields.Many2one('res.lang', related='mobikul_app.default_lang', string='Default Language') + currency_id = fields.Many2one('res.currency', related='mobikul_app.currency_id', string='Default Currency') + pricelist_id = fields.Many2one('product.pricelist', related='mobikul_app.pricelist_id', string='Default Pricelist') + mobikul_reset_password = fields.Boolean(string='Enable password reset', help="This allows users to trigger a password reset from App") + mobikul_signup = fields.Boolean(string='Enable customer sign up') + mobikul_allow_guest = fields.Boolean(string='Allow Guests to view products on App.') + mobikul_signup_template_user_id = fields.Many2one('res.users', string='Template user for new users created through App') + module_auth_oauth = fields.Boolean(string='Allow social login (Gmail,Facebook,etc)', help="Use external authentication providers (OAuth)") + mobikul_gmail_signin = fields.Boolean(string='Gmail SignIn') + mobikul_facebook_signin = fields.Boolean(string='Facebook SignIn') + mobikul_twitter_signin = fields.Boolean(string='Twitter SignIn') + # module_website_wishlist = fields.Boolean(string='Allow Wishlist feature on App.', help="Use external Addon to add wishlist feature in website") + module_wk_review = fields.Boolean(string='Allow Product Review feature on App.', help="Use external Addon to add review feature in website") + module_odoo_marketplace = fields.Boolean(string='Allow Odoo Marketplace on App.', help="Use external Addon to add Multi Vendor Marketplace in website") + module_website_sale_wishlist = fields.Boolean(string='Allow Website Sale Wishlist on App.', + help="Use default Addon to add Wishlist in website") + + @api.onchange('pricelist_id') + def onchange_currency_id_set(self): + self.currency_id = self.pricelist_id.currency_id + + + @api.model + def get_values(self): + res = super(MobikulConfigSettings, self).get_values() + IrConfigParam = self.env['ir.config_parameter'] + # we use safe_eval on the result, since the value of the parameter is a nonempty string + res.update( + mobikul_reset_password=safe_eval(IrConfigParam.get_param('auth_signup.reset_password', 'False')), + mobikul_signup = safe_eval(IrConfigParam.get_param('auth_signup.allow_uninvited', 'False')), + mobikul_signup_template_user_id= safe_eval(IrConfigParam.get_param('auth_signup.template_user_id', 'False')), + mobikul_allow_guest= safe_eval(IrConfigParam.get_param('mobikul.allow_guest', 'False')), + mobikul_gmail_signin= safe_eval(IrConfigParam.get_param('mobikul.gmail_signin', 'False')), + mobikul_facebook_signin= safe_eval(IrConfigParam.get_param('mobikul.facebook_signin', 'False')), + mobikul_twitter_signin= safe_eval(IrConfigParam.get_param('mobikul.twitter_signin', 'False')), + ) + return res + + @api.multi + def set_values(self): + self.ensure_one() + super(MobikulConfigSettings, self).set_values() + IrConfigParam = self.env['ir.config_parameter'] + # we store the repr of the values, since the value of the parameter is a required string + IrConfigParam.set_param('auth_signup.reset_password', repr(self.mobikul_reset_password)) + IrConfigParam.set_param('auth_signup.allow_uninvited', repr(self.mobikul_signup)) + IrConfigParam.set_param('auth_signup.template_user_id', repr(self.mobikul_signup_template_user_id.id)) + IrConfigParam.set_param('mobikul.allow_guest', repr(self.mobikul_allow_guest)) + IrConfigParam.set_param('mobikul.gmail_signin', repr(self.mobikul_gmail_signin)) + IrConfigParam.set_param('mobikul.facebook_signin', repr(self.mobikul_facebook_signin)) + IrConfigParam.set_param('mobikul.twitter_signin', repr(self.mobikul_twitter_signin)) diff --git a/ext/3rd-party-addons/mobikul/models/sale_order.py b/ext/3rd-party-addons/mobikul/models/sale_order.py new file mode 100755 index 00000000..7e8f5c0a --- /dev/null +++ b/ext/3rd-party-addons/mobikul/models/sale_order.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +########################################################################## +# +# Copyright (c) 2015-Present Webkul Software Pvt. Ltd. () +# +########################################################################## +import logging +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError +_logger = logging.getLogger(__name__) + +class ResPartner(models.Model): + _inherit = 'res.partner' + + last_mobikul_so_id = fields.Many2one('sale.order', string='Last Order from Mobikul App') + banner_image = fields.Binary('Banner Image', attachment=True) + token_ids = fields.One2many('fcm.registered.devices', 'customer_id', string='Registered Devices',readonly=True) + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + cart_count = fields.Integer(compute='_compute_cart_count', string='Cart Quantity') + + @api.multi + @api.depends('order_line.product_uom_qty', 'order_line.product_id') + def _compute_cart_count(self): + is_wesiteSaleDelivery = self.env['mobikul'].sudo().check_mobikul_addons().get('website_sale_delivery') + for order in self: + if is_wesiteSaleDelivery: + order.cart_count = int(sum([line.product_uom_qty for line in order.order_line if not line.is_delivery])) + else: + order.cart_count = int(sum(order.mapped('order_line.product_uom_qty'))) + + + +class PaymentAcquirer(models.Model): + _inherit = 'payment.acquirer' + + is_mobikul_available = fields.Boolean( + 'Visible in Portal / Website', copy=False, + help="Make this payment acquirer available on App") + mobikul_reference_code = fields.Char( + 'Mobikul Reference Code', copy=False, + help="Unique Code in order to integrate it with Mobikul App.") + mobikul_pre_msg = fields.Text('Message to Display', copy=False, translate=True,help="this field is depricated from mobikul") diff --git a/ext/3rd-party-addons/mobikul/security/ir.model.access.csv b/ext/3rd-party-addons/mobikul/security/ir.model.access.csv new file mode 100755 index 00000000..1f2c0405 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/security/ir.model.access.csv @@ -0,0 +1,20 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_mobikul_banners_public,mobikul.mobikul_banners.public,model_mobikul_banners,,1,0,0,0 +access_mobikul_category_public,mobikul.mobikul_category.public,model_mobikul_category,,1,0,0,0 +access_res_partner_public,base.res_partner.public,base.model_res_partner,,1,0,0,0 +access_mobikul_push_notification_public,mobikul.mobikul_push_notification.public,model_mobikul_push_notification,,1,0,0,0 +access_mobikul_push_notification_template_public,mobikul.mobikul_push_notification_template.public,model_mobikul_push_notification_template,,1,0,0,0 +access_mobikul_notification_messages_template_public,mobikul.mobikul_notification_messages.public,model_mobikul_notification_messages,,1,0,0,0 + +access_mobikul_category,Mobikul Category Access,model_mobikul_category,mobikul.group_mobikul_user,1,1,1,1 +access_mobikul_banners,Mobikul Banners Access,model_mobikul_banners,mobikul.group_mobikul_user,1,1,1,1 +access_mobikul_product_slider,Mobikul Product Slider Access,model_mobikul_product_slider,mobikul.group_mobikul_user,1,1,1,1 +access_mobikul_push_notification,Mobikul Push Notification Access,model_mobikul_push_notification,mobikul.group_mobikul_user,1,1,1,1 +access_mobikul_notification_messages,Mobikul Notification Messages Access,model_mobikul_notification_messages,mobikul.group_mobikul_user,1,0,0,0 + +access_mobikul_push_notification_template,Mobikul Push Notification Template Access,model_mobikul_push_notification_template,mobikul.group_mobikul_user,1,0,0,0 + +access_manager_mobikul_mobikul,Mobikul Access,model_mobikul,mobikul.group_mobikul_manager,1,1,1,1 +access_manager_mobikul_push_notification,Mobikul Push Notification Access,model_mobikul_push_notification,mobikul.group_mobikul_manager,1,1,1,1 +access_manager_mobikul_push_notification_template,Mobikul Push Notification Template Access,model_mobikul_push_notification_template,mobikul.group_mobikul_manager,1,1,1,1 +access_manager_mobikul_notification_messages,Mobikul Push Notification Messages Access,model_mobikul_notification_messages,mobikul.group_mobikul_manager,1,1,1,1 \ No newline at end of file diff --git a/ext/3rd-party-addons/mobikul/security/mobikul_security.xml b/ext/3rd-party-addons/mobikul/security/mobikul_security.xml new file mode 100755 index 00000000..eb279a84 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/security/mobikul_security.xml @@ -0,0 +1,24 @@ + + + + + Mobikul App + Mobikul-App Group + 10 + + + + User + All access except Mobikul Configuration. + + + + + Manager + Full Access + + + + + + \ No newline at end of file diff --git a/ext/3rd-party-addons/mobikul/static/description/1.png b/ext/3rd-party-addons/mobikul/static/description/1.png new file mode 100755 index 00000000..02d213c7 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/description/1.png differ diff --git a/ext/3rd-party-addons/mobikul/static/description/2.png b/ext/3rd-party-addons/mobikul/static/description/2.png new file mode 100755 index 00000000..d294bd50 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/description/2.png differ diff --git a/ext/3rd-party-addons/mobikul/static/description/3.png b/ext/3rd-party-addons/mobikul/static/description/3.png new file mode 100755 index 00000000..87b173b6 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/description/3.png differ diff --git a/ext/3rd-party-addons/mobikul/static/description/4.png b/ext/3rd-party-addons/mobikul/static/description/4.png new file mode 100755 index 00000000..73cfe16f Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/description/4.png differ diff --git a/ext/3rd-party-addons/mobikul/static/description/5.png b/ext/3rd-party-addons/mobikul/static/description/5.png new file mode 100755 index 00000000..9c8eb5b5 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/description/5.png differ diff --git a/ext/3rd-party-addons/mobikul/static/description/Banner.png b/ext/3rd-party-addons/mobikul/static/description/Banner.png new file mode 100755 index 00000000..f111ae72 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/description/Banner.png differ diff --git a/ext/3rd-party-addons/mobikul/static/description/icon-features.png b/ext/3rd-party-addons/mobikul/static/description/icon-features.png new file mode 100755 index 00000000..10e23486 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/description/icon-features.png differ diff --git a/ext/3rd-party-addons/mobikul/static/description/icon-help.png b/ext/3rd-party-addons/mobikul/static/description/icon-help.png new file mode 100755 index 00000000..303f9e24 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/description/icon-help.png differ diff --git a/ext/3rd-party-addons/mobikul/static/description/icon-support.png b/ext/3rd-party-addons/mobikul/static/description/icon-support.png new file mode 100755 index 00000000..5f975909 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/description/icon-support.png differ diff --git a/ext/3rd-party-addons/mobikul/static/description/icon.png b/ext/3rd-party-addons/mobikul/static/description/icon.png new file mode 100755 index 00000000..40cb3bc4 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/description/icon.png differ diff --git a/ext/3rd-party-addons/mobikul/static/description/index.html b/ext/3rd-party-addons/mobikul/static/description/index.html new file mode 100755 index 00000000..9529d6a7 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/static/description/index.html @@ -0,0 +1,496 @@ +
+
+

Mobikul Mobile App Builder for Odoo eCommerce(website)

+

Convert your Odoo eCommerce(website) store in the native mobile application.

+ +
+ +
+
+
+ + + + + +
+
+

About Mobikul: Mobile App Builder

+
+ +
+
+

+ If you are running your eCommerce(website) on Odoo, and looking for the native mobile application then you are at right place. +

+

+ Mobikul Mobile App Builder for Odoo will convert your Odoo store in the native mobile applications. It is not necessary to have desktops/laptops to shop from your store. The Mobile App is fully compatible with default Odoo store. +

+

+ The mobile application provides a user-friendly experience and enhances the customer`s engagement over the mobile platform. +

+

+ Now the customers can visit your store by using their mobile only and can shop on the Go. +

+

+ So what you waiting for, you must have a mobile app with great features & functionalities for your Odoo store. +

+
+

+ Need Demo ?? Check following links... +

+
+
+
+ +
+
+

+ + Install App on your Phone(Google PlayStore Link) +
+
+ + Manage App from Odoo(Backend Odoo Link) +

+

( Please don`t forget to share your reviews on playstore & here as well. Your opinions, comments and suggestions are VERY Important to keep this app updated and more usefull !!! )

+
+
+ +
+
+

Default Features

+

Packed with many default features which a mobile app must have like:

+
+
+ +
+
+
+ +

+ One Time Setup +

+

+ Just set up this app once in your odoo back-end and you are ready to go +

+
+
+ +

+ Fully Configurable +

+

+ Configure this App from Odoo back-end. +

+
+
+
+
+
+ +
+
+
+ +

+ Supports all types of Products +

+

+ All types of products are supported i.e with or without attributes like color,size, etc +

+
+
+ +

+ Publish products seperately from Website +

+

+ Option to published/un-published products/categories on App separately from Website. +

+
+
+ +

+ Product Multi-images +

+

+ Product can have more than one images, with Zoom-in,Zoom-out feature.Optimized images for faster loading. +

+
+
+
+
+
+
+ +

+ Offer Discounts +

+

+ Via Odoo pricelist, you may have special discount on your product(s) if purchased from App only or vice-versa. +

+
+
+ +

+ Distinguish b/w orders placed from App & Website +

+

+ you can keep tracking from which plateform(Website/App) your customers bought your products more. +

+
+
+ + +

+ (new) + Supports Multilingual +

+

+ Manage the translations for the components in your app from backend +

+ +
+
+
+ +
+
+ +
+ +

+ Fast Support +

+

+ We offer extremely convenient and quick support service to all your technical help & support requests. +

+
+ +
+
+ +
+
+

Bonus: Extra Added Features

+

To give an extra spark we have bundled some more features like:

+
+
+ + + +
+
+
+
+ +
+
+

+ Banners +

+

+ Manage Unlimited Banners from Odoo. +

+

+ Configure what to trigger when customer click on it - a product or a category or a custom collection page. +

+

+ Manage Publish/Expiration date for every banner seperately. +

+ +
+
+
+
+ +
+
+
+

+ Product Sliders +

+

+ Manage Unlimited Product Sliders like New Products, Hot Deals, etc. +

+

+ Configure the selection criteria of products - automatic(new products, etc) or manual. +

+

+ Manage slider mode i.e fixed or sliding +

+
+
+ +
+
+
+ +
+
+
+
+ +
+
+

+ Push Notifications +

+

+ Manage Unlimited Push Notifications from Odoo, both token/topic based. +

+

+ Pushed automatically to all/specific connected devices. +

+

+ Configure what to trigger when customer click on it - a product or a category or a custom collection page. +

+ +
+
+
+
+ +
+
+
+

+ Featured Category +

+

+ Manage Unlimited Featured Category. +

+

+ Highlight popular category on homepage, by making them Featured from Odoo. +

+

+ Convert any category to featured or vice-versa from Odoo. +

+
+
+ +
+
+
+ +
+ +
+ +
+
+

Extra Information

+
+

+ On purchasing this module, you`ll get .apk without Source Code(Need to create a Ticket). +

+
  • Free Installation/Setup for first 50 customers.
  • +
  • Create a Ticket with all your Store Details: Click me
  • +
  • If you want Source Code as well, you need to pay +$599.00 extra
+

+ You can publish this App through Webkul Google Play Store Account. +

+
  • If you want to publish App with your Google Play Store Account, you need to pay +$50.00 extra
+

+ You will get 90 days FREE support for any doubt, queries, and bug fixing (excluding data recovery) or any type of issue related to this module. +

  • You may extend support later(PAID).
+

+
+
+
+ + + +
+
+
+

Help and Support

+
+
+ +
+
+ +
+
+ + + +
+
diff --git a/ext/3rd-party-addons/mobikul/static/description/main.png b/ext/3rd-party-addons/mobikul/static/description/main.png new file mode 100755 index 00000000..a11291b1 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/description/main.png differ diff --git a/ext/3rd-party-addons/mobikul/static/description/mobikul.gif b/ext/3rd-party-addons/mobikul/static/description/mobikul.gif new file mode 100755 index 00000000..a187a2c2 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/description/mobikul.gif differ diff --git a/ext/3rd-party-addons/mobikul/static/src/img/Demo-Banner-1.png b/ext/3rd-party-addons/mobikul/static/src/img/Demo-Banner-1.png new file mode 100755 index 00000000..b0cec692 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/src/img/Demo-Banner-1.png differ diff --git a/ext/3rd-party-addons/mobikul/static/src/img/Demo-Banner-2.png b/ext/3rd-party-addons/mobikul/static/src/img/Demo-Banner-2.png new file mode 100755 index 00000000..fcbf306f Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/src/img/Demo-Banner-2.png differ diff --git a/ext/3rd-party-addons/mobikul/static/src/img/Order-Placed.png b/ext/3rd-party-addons/mobikul/static/src/img/Order-Placed.png new file mode 100755 index 00000000..43148472 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/src/img/Order-Placed.png differ diff --git a/ext/3rd-party-addons/mobikul/static/src/img/Sign-up.png b/ext/3rd-party-addons/mobikul/static/src/img/Sign-up.png new file mode 100755 index 00000000..d71546f1 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/src/img/Sign-up.png differ diff --git a/ext/3rd-party-addons/mobikul/static/src/img/ban1.jpg b/ext/3rd-party-addons/mobikul/static/src/img/ban1.jpg new file mode 100755 index 00000000..e55f8f59 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/src/img/ban1.jpg differ diff --git a/ext/3rd-party-addons/mobikul/static/src/img/ban2.jpg b/ext/3rd-party-addons/mobikul/static/src/img/ban2.jpg new file mode 100755 index 00000000..09a1b424 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/src/img/ban2.jpg differ diff --git a/ext/3rd-party-addons/mobikul/static/src/img/icon.png b/ext/3rd-party-addons/mobikul/static/src/img/icon.png new file mode 100755 index 00000000..40cb3bc4 Binary files /dev/null and b/ext/3rd-party-addons/mobikul/static/src/img/icon.png differ diff --git a/ext/3rd-party-addons/mobikul/sync_categories.py b/ext/3rd-party-addons/mobikul/sync_categories.py new file mode 100755 index 00000000..75e93057 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/sync_categories.py @@ -0,0 +1,72 @@ +# import xmlrpclib +# import sys + +# url = "http://mohitg:8010/"; +# dbname = "mobikul"; +# username = "admin"; +# pwd = "webkul"; + +# sock_common = xmlrpclib.ServerProxy(url+'xmlrpc/common') +# sock = xmlrpclib.ServerProxy(url+'xmlrpc/object') +# try: +# uid = sock_common.login(dbname, username, pwd) +# print uid +# except Exception as e: +# print "Error in Connection %r"%e +# sys.exit() + +# def sync_w2m(): +# wc_ids = sock.execute(dbname, uid, pwd, 'product.public.category', 'search_read', [],['name','image','parent_id']) +# mapping = {} +# #creating categories without child/parent +# for wc in wc_ids: +# try: +# mc = sock.execute(dbname, uid, pwd, 'mobikul.category', 'create',{'name':wc['name'],'image':wc['image']}) +# mapping[wc['id']]=mc +# except Exception,e: +# print "1) Error in WC-ID:%r"%wc['id'] +# print "Detail:%r"%e +# #updating categories with child/parent +# for wc in wc_ids: +# if wc.get('parent_id'): +# try: +# sock.execute(dbname, uid, pwd, 'mobikul.category', 'write',mapping.get(wc['id']), {'parent_id':mapping.get(wc['parent_id'][0])}) +# except Exception,e: +# print "2) Error in WC-ID:%r"%wc['parent_id'] +# print "Detail:%r"%e +# #linking products with categories +# p_ids = sock.execute(dbname, uid, pwd, 'product.template', 'search_read', [],['public_categ_ids']) +# for p in p_ids: +# mcids = [mapping.get(a) for a in p['public_categ_ids']] +# if mcids: +# try: +# sock.execute(dbname, uid, pwd, 'product.template', 'write',p['id'],{'mobikul_categ_ids':[[6,0,mcids]]}) +# except Exception,e: +# print "3) Error in P-ID:%r"%p['id'] +# print "Detail:%r"%e +# # break + +# def getBase64(key,pwd): +# cred = {'login' : key, 'pwd' : pwd} +# encoded_cred = str(cred).encode('base64','strict') +# print encoded_cred +# from base64 import b64decode +# decoded_cred = b64decode(encoded_cred) +# print decoded_cred + +# if __name__=='__main__': +# print "Start" +# print sock.execute(dbname, uid, pwd, 'res.users', 'search_read', [('id','=',36)],['name','password','new_password']) +# print sock.execute(dbname, uid, pwd, 'res.users', 'write', 36,{'password':'webkul'}) +# print sock.execute(dbname, uid, pwd, 'res.users', 'search_read', [('id','=',36)],['name','password','new_password']) +# # print sync_w2m() +# # getBase64('admin','webkul') + +to_data = { + "to":"False" +} + +if not to_data.get("to",False) and not to_data.get("registration_ids",False): + print (True) +else: + print (False) diff --git a/ext/3rd-party-addons/mobikul/views/delivery_view.xml b/ext/3rd-party-addons/mobikul/views/delivery_view.xml new file mode 100755 index 00000000..20140d3f --- /dev/null +++ b/ext/3rd-party-addons/mobikul/views/delivery_view.xml @@ -0,0 +1,26 @@ + diff --git a/ext/3rd-party-addons/mobikul/views/menus.xml b/ext/3rd-party-addons/mobikul/views/menus.xml new file mode 100755 index 00000000..70422db1 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/views/menus.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/3rd-party-addons/mobikul/views/mobikul_views.xml b/ext/3rd-party-addons/mobikul/views/mobikul_views.xml new file mode 100755 index 00000000..1fdf831b --- /dev/null +++ b/ext/3rd-party-addons/mobikul/views/mobikul_views.xml @@ -0,0 +1,802 @@ + + + + fcm_registered_devices.form + fcm.registered.devices + +
+ +
+ +
+ + + + + + + + + + + + +
+
+
+
+ + + fcm_registered_devices.tree + fcm.registered.devices + + + + + + + + + + + + FCM Registered Devices + fcm.registered.devices + tree,form + form + + + + + fcm_registered_topics.tree + fcm.registered.topics + + + + + + + + + FCM Registered Topics + fcm.registered.topics + tree + form + + + + + mobikul.push.notification.form + mobikul.push.notification + +
+
+
+ +
+ +
+ +
+
+
+

+ +

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + mobikul.push.notification.tree + mobikul.push.notification + + + + + + + + + + + + mobikul_push_notification.search.mobikul + mobikul.push.notification + + + + + + + + + + + + + Mobikul Push Notifications + mobikul.push.notification + tree,form + form + + + +

+ Click to add a new Push Notification for your Mobikul App. +

+
+
+ + + mobikul.push.notification.template.form + mobikul.push.notification.template + +
+ +
+ +
+ +
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + +
+
+
+
+
+

Change Banner Image

+
+
+

Upload Banner Image

+
+ +
+ +
+
+ + + mobikul.banners.tree + mobikul.banners + + + + + + + + + + + + + mobikul_banners.search.mobikul + mobikul.banners + + + + + + + + + + + + Mobikul Banners + mobikul.banners + tree,form + form + + + +

+ Click to add a new Banner for your Mobikul App. +

+
+
+ + + mobikul.category.form + mobikul.category + +
+ +
+ + +
+ +
+
+
+

+ +

+
+ + + + + + +
+
+
+
+ + + mobikul.category.tree + mobikul.category + + + + + + + + + + + mobikul.category.tree2 + mobikul.category + + + + + + + + + + + mobikul.category.search.mobikul + mobikul.category + + + + + + + + + + + + + Mobikul Category + mobikul.category + tree,form + form + + + +

+ Click to add a new Category for your Mobikul App. +

+
+
+ + + + Mobikul Featured Category + mobikul.category + tree,form + form + + {'search_default_is_featured': 1, 'default_type': 'featured'} + +

+ Click to add a new Featured Category for your Mobikul App. +

+
+
+ + + mobikul.form + mobikul + +
+ +
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + mobikul.tree + mobikul + + + + + + + + + + Mobikul-App Configuration + mobikul + tree,form + form + + + + + + + mobikul_product_slider.form + mobikul.product.slider + +
+ +
+ + +
+
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + mobikul_product_slider.tree + mobikul.product.slider + + + + + + + + + + + + mobikul_product_slider.search.mobikul + mobikul.product.slider + + + + + + + + + + + + + + + Product Slider + mobikul.product.slider + tree,form + form + + + +

+ Click to add a new Product Slider for your Mobikul App. +

+
+
+ + + + + mobikul_notification_massages.tree + mobikul.notification.messages + + + + + + + + + + + + + mobikul_notification_massages.form + mobikul.notification.messages + +
+ +
+ +
+
+
+
+

+ +

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+

Change Banner Image

+
+
+

Upload Banner Image

+
+ +
+
+
+
+ + + Mobikul Notification Messages Search + mobikul.notification.messages + + + + + + + + + + + + Mobikul Notification Messages Action + mobikul.notification.messages + tree,form + form + + {'search_default_group_customer_id': 1} + + + +

+ Click to add a new Notification Messages for your Mobikul App. +

+
+
+ + +
diff --git a/ext/3rd-party-addons/mobikul/views/order_view.xml b/ext/3rd-party-addons/mobikul/views/order_view.xml new file mode 100755 index 00000000..2fe14160 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/views/order_view.xml @@ -0,0 +1,71 @@ + + + + mobikul.acquirer_form + payment.acquirer + + + + + + + + + + + + + + + + sale.order.mobikul.search.view + sale.order + + + + + + + + + + Mobikul Orders + sale.order + tree,form + [('state', 'in', ('sale', 'done'))] + {'show_sale': True, 'search_default_from_mobikul': 1} + + You don't have any confirmed order from the Mobikul App. + + + + Unpaid Mobikul Orders + sale.order + tree,form + [('state', '=', 'sent')] + {'show_sale': True, 'search_default_from_mobikul': 1} + + You don't have any unpaid order from the Mobikul App. + + + + Cancelled Orders + sale.order + tree,form + [('state', '=', 'cancel')] + {'show_sale': True, 'search_default_from_mobikul': 1} + + You don't have any cancelled order from the Mobikul App. + + + + Invoices + account.invoice + form + tree,form + [('team_id.mobikul_ids', '!=', False)] + + {'type':'out_invoice'} + + + \ No newline at end of file diff --git a/ext/3rd-party-addons/mobikul/views/product_view.xml b/ext/3rd-party-addons/mobikul/views/product_view.xml new file mode 100755 index 00000000..788b7b4b --- /dev/null +++ b/ext/3rd-party-addons/mobikul/views/product_view.xml @@ -0,0 +1,69 @@ + + + + res.partner.form.mobikul + res.partner + + + + + + + + + + + + + + + + + product.template.search.mobikul + product.template + + + + + + + + + + + + + product.template.product.mobikul.form + product.template + + + + + + + + + + +
+ +
+
+
+ + Products + product.template + form + kanban,tree,form + {'search_default_mobikul_published': 1} + +
diff --git a/ext/3rd-party-addons/mobikul/views/res_config_view.xml b/ext/3rd-party-addons/mobikul/views/res_config_view.xml new file mode 100755 index 00000000..8bd98706 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/views/res_config_view.xml @@ -0,0 +1,140 @@ + + + + Mobikul Settings + mobikul.config.settings + +
+
+
+
+ +
+ +
+
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + Mobikul Settings + mobikul.config.settings + form + inline + +
diff --git a/ext/3rd-party-addons/mobikul/views/sync_cat_view.xml b/ext/3rd-party-addons/mobikul/views/sync_cat_view.xml new file mode 100755 index 00000000..06efa059 --- /dev/null +++ b/ext/3rd-party-addons/mobikul/views/sync_cat_view.xml @@ -0,0 +1,36 @@ + + + + mobikul_cat_sync_form + mobikul.sync.category + form + +
+ +
+

Sync category with Website category

+ + + + + +
+ + +
+
+ +
+
+ + + + + Mobikul Sync Category + mobikul.sync.category + form + form + new + +
\ No newline at end of file