@@ -0,0 +1,7 @@ | |||
.DS_Store | |||
*.pyc | |||
*.egg-info | |||
*.swp | |||
tags | |||
node_modules | |||
__pycache__ |
@@ -0,0 +1,8 @@ | |||
# Default ignored files | |||
/shelf/ | |||
/workspace.xml | |||
# Editor-based HTTP Client requests | |||
/httpRequests/ | |||
# Datasource local storage ignored files | |||
/dataSources/ | |||
/dataSources.local.xml |
@@ -0,0 +1,8 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<module type="WEB_MODULE" version="4"> | |||
<component name="NewModuleRootManager"> | |||
<content url="file://$MODULE_DIR$" /> | |||
<orderEntry type="inheritedJdk" /> | |||
<orderEntry type="sourceFolder" forTests="false" /> | |||
</component> | |||
</module> |
@@ -0,0 +1,8 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project version="4"> | |||
<component name="ProjectModuleManager"> | |||
<modules> | |||
<module fileurl="file://$PROJECT_DIR$/.idea/invoice_sync.iml" filepath="$PROJECT_DIR$/.idea/invoice_sync.iml" /> | |||
</modules> | |||
</component> | |||
</project> |
@@ -0,0 +1,19 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project version="4"> | |||
<component name="MessDetectorOptionsConfiguration"> | |||
<option name="transferred" value="true" /> | |||
</component> | |||
<component name="PHPCSFixerOptionsConfiguration"> | |||
<option name="transferred" value="true" /> | |||
</component> | |||
<component name="PHPCodeSnifferOptionsConfiguration"> | |||
<option name="highlightLevel" value="WARNING" /> | |||
<option name="transferred" value="true" /> | |||
</component> | |||
<component name="PhpStanOptionsConfiguration"> | |||
<option name="transferred" value="true" /> | |||
</component> | |||
<component name="PsalmOptionsConfiguration"> | |||
<option name="transferred" value="true" /> | |||
</component> | |||
</project> |
@@ -0,0 +1,6 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project version="4"> | |||
<component name="VcsDirectoryMappings"> | |||
<mapping directory="$PROJECT_DIR$" vcs="Git" /> | |||
</component> | |||
</project> |
@@ -0,0 +1 @@ | |||
Invoice Sync |
@@ -0,0 +1,3 @@ | |||
__version__ = '0.0.1' | |||
@@ -0,0 +1,221 @@ | |||
app_name = "invoice_sync" | |||
app_title = "Invoice Sync" | |||
app_publisher = "SolutionERP" | |||
app_description = "Syncing invoice with mobile app" | |||
app_email = "support@solutionerp.com" | |||
app_license = "mit" | |||
# required_apps = [] | |||
# Includes in <head> | |||
# ------------------ | |||
# include js, css files in header of desk.html | |||
# app_include_css = "/assets/invoice_sync/css/invoice_sync.css" | |||
# app_include_js = "/assets/invoice_sync/js/invoice_sync.js" | |||
# include js, css files in header of web template | |||
# web_include_css = "/assets/invoice_sync/css/invoice_sync.css" | |||
# web_include_js = "/assets/invoice_sync/js/invoice_sync.js" | |||
# include custom scss in every website theme (without file extension ".scss") | |||
# website_theme_scss = "invoice_sync/public/scss/website" | |||
# include js, css files in header of web form | |||
# webform_include_js = {"doctype": "public/js/doctype.js"} | |||
# webform_include_css = {"doctype": "public/css/doctype.css"} | |||
# include js in page | |||
# page_js = {"page" : "public/js/file.js"} | |||
# include js in doctype views | |||
# doctype_js = {"doctype" : "public/js/doctype.js"} | |||
# doctype_list_js = {"doctype" : "public/js/doctype_list.js"} | |||
# doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} | |||
# doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"} | |||
# Svg Icons | |||
# ------------------ | |||
# include app icons in desk | |||
# app_include_icons = "invoice_sync/public/icons.svg" | |||
# Home Pages | |||
# ---------- | |||
# application home page (will override Website Settings) | |||
# home_page = "login" | |||
# website user home page (by Role) | |||
# role_home_page = { | |||
# "Role": "home_page" | |||
# } | |||
# Generators | |||
# ---------- | |||
# automatically create page for each record of this doctype | |||
# website_generators = ["Web Page"] | |||
# Jinja | |||
# ---------- | |||
# add methods and filters to jinja environment | |||
# jinja = { | |||
# "methods": "invoice_sync.utils.jinja_methods", | |||
# "filters": "invoice_sync.utils.jinja_filters" | |||
# } | |||
# Installation | |||
# ------------ | |||
# before_install = "invoice_sync.install.before_install" | |||
# after_install = "invoice_sync.install.after_install" | |||
# Uninstallation | |||
# ------------ | |||
# before_uninstall = "invoice_sync.uninstall.before_uninstall" | |||
# after_uninstall = "invoice_sync.uninstall.after_uninstall" | |||
# Integration Setup | |||
# ------------------ | |||
# To set up dependencies/integrations with other apps | |||
# Name of the app being installed is passed as an argument | |||
# before_app_install = "invoice_sync.utils.before_app_install" | |||
# after_app_install = "invoice_sync.utils.after_app_install" | |||
# Integration Cleanup | |||
# ------------------- | |||
# To clean up dependencies/integrations with other apps | |||
# Name of the app being uninstalled is passed as an argument | |||
# before_app_uninstall = "invoice_sync.utils.before_app_uninstall" | |||
# after_app_uninstall = "invoice_sync.utils.after_app_uninstall" | |||
# Desk Notifications | |||
# ------------------ | |||
# See xhiveframework.core.notifications.get_notification_config | |||
# notification_config = "invoice_sync.notifications.get_notification_config" | |||
# Permissions | |||
# ----------- | |||
# Permissions evaluated in scripted ways | |||
# permission_query_conditions = { | |||
# "Event": "xhiveframework.desk.doctype.event.event.get_permission_query_conditions", | |||
# } | |||
# | |||
# has_permission = { | |||
# "Event": "xhiveframework.desk.doctype.event.event.has_permission", | |||
# } | |||
# DocType Class | |||
# --------------- | |||
# Override standard doctype classes | |||
# override_doctype_class = { | |||
# "ToDo": "custom_app.overrides.CustomToDo" | |||
# } | |||
# Document Events | |||
# --------------- | |||
# Hook on document methods and events | |||
# doc_events = { | |||
# "*": { | |||
# "on_update": "method", | |||
# "on_cancel": "method", | |||
# "on_trash": "method" | |||
# } | |||
# } | |||
# Scheduled Tasks | |||
# --------------- | |||
# scheduler_events = { | |||
# "all": [ | |||
# "invoice_sync.tasks.all" | |||
# ], | |||
# "daily": [ | |||
# "invoice_sync.tasks.daily" | |||
# ], | |||
# "hourly": [ | |||
# "invoice_sync.tasks.hourly" | |||
# ], | |||
# "weekly": [ | |||
# "invoice_sync.tasks.weekly" | |||
# ], | |||
# "monthly": [ | |||
# "invoice_sync.tasks.monthly" | |||
# ], | |||
# } | |||
# Testing | |||
# ------- | |||
# before_tests = "invoice_sync.install.before_tests" | |||
# Overriding Methods | |||
# ------------------------------ | |||
# | |||
# override_whitelisted_methods = { | |||
# "xhiveframework.desk.doctype.event.event.get_events": "invoice_sync.event.get_events" | |||
# } | |||
# | |||
# each overriding function accepts a `data` argument; | |||
# generated from the base implementation of the doctype dashboard, | |||
# along with any modifications made in other Xhive apps | |||
# override_doctype_dashboards = { | |||
# "Task": "invoice_sync.task.get_dashboard_data" | |||
# } | |||
# exempt linked doctypes from being automatically cancelled | |||
# | |||
# auto_cancel_exempted_doctypes = ["Auto Repeat"] | |||
# Ignore links to specified DocTypes when deleting documents | |||
# ----------------------------------------------------------- | |||
# ignore_links_on_delete = ["Communication", "ToDo"] | |||
# Request Events | |||
# ---------------- | |||
# before_request = ["invoice_sync.utils.before_request"] | |||
# after_request = ["invoice_sync.utils.after_request"] | |||
# Job Events | |||
# ---------- | |||
# before_job = ["invoice_sync.utils.before_job"] | |||
# after_job = ["invoice_sync.utils.after_job"] | |||
# User Data Protection | |||
# -------------------- | |||
# user_data_fields = [ | |||
# { | |||
# "doctype": "{doctype_1}", | |||
# "filter_by": "{filter_by}", | |||
# "redact_fields": ["{field_1}", "{field_2}"], | |||
# "partial": 1, | |||
# }, | |||
# { | |||
# "doctype": "{doctype_2}", | |||
# "filter_by": "{filter_by}", | |||
# "partial": 1, | |||
# }, | |||
# { | |||
# "doctype": "{doctype_3}", | |||
# "strict": False, | |||
# }, | |||
# { | |||
# "doctype": "{doctype_4}" | |||
# } | |||
# ] | |||
# Authentication and authorization | |||
# -------------------------------- | |||
# auth_hooks = [ | |||
# "invoice_sync.auth.validate" | |||
# ] |
@@ -0,0 +1,253 @@ | |||
import requests | |||
import json | |||
import xhiveframework | |||
import json | |||
import urllib.parse; | |||
import base64 | |||
from werkzeug.wrappers import Response | |||
@xhiveframework.whitelist(allow_guest=True) | |||
def generate_token_secure( api_key, api_secret, app_key): | |||
try: | |||
try: | |||
app_key = base64.b64decode(app_key).decode("utf-8") | |||
except Exception as e: | |||
return Response(json.dumps({"message": "Security Parameters are not valid" , "user_count": 0}), status=401, mimetype='application/json') | |||
clientID, clientSecret, clientUser = xhiveframework.db.get_value('OAuth Client', {'app_name': app_key}, ['client_id', 'client_secret','user']) | |||
if clientID is None: | |||
# return app_key | |||
return Response(json.dumps({"message": "Security Parameters are not valid" , "user_count": 0}), status=401, mimetype='application/json') | |||
client_id = clientID # Replace with your OAuth client ID | |||
client_secret = clientSecret # Replace with your OAuth client secret | |||
url = xhiveframework.local.conf.host_name + "/api/method/xhiveframework.integrations.oauth2.get_token" | |||
payload = { | |||
"username": api_key, | |||
"password": api_secret, | |||
"grant_type": "password", | |||
"client_id": client_id, | |||
"client_secret": client_secret, | |||
} | |||
files = [] | |||
headers = {"Content-Type": "application/json"} | |||
response = requests.request("POST", url, data=payload, files=files) | |||
if response.status_code == 200: | |||
result_data = json.loads(response.text) | |||
return Response(json.dumps({"data":result_data}), status=200, mimetype='application/json') | |||
else: | |||
xhiveframework.local.response.http_status_code = 401 | |||
return json.loads(response.text) | |||
except Exception as e: | |||
return Response(json.dumps({"message": e , "user_count": 0}), status=500, mimetype='application/json') | |||
@xhiveframework.whitelist(allow_guest=True) | |||
def create_refresh_token(refresh_token): | |||
url = xhiveframework.local.conf.host_name + "/api/method/xhiveframework.integrations.oauth2.get_token" | |||
payload = f'grant_type=refresh_token&refresh_token={refresh_token}' | |||
headers = { | |||
'Content-Type': 'application/x-www-form-urlencoded' | |||
} | |||
files = [] | |||
response = requests.post(url, headers=headers, data=payload, files=files) | |||
if response.status_code == 200: | |||
try: | |||
message_json = json.loads(response.text) | |||
new_message = { | |||
"access_token": message_json["access_token"], | |||
"expires_in": message_json["expires_in"], | |||
"token_type": message_json["token_type"], | |||
"scope": message_json["scope"], | |||
"refresh_token": message_json["refresh_token"] | |||
} | |||
return Response(json.dumps({"data": new_message}), status=200, mimetype='application/json') | |||
except json.JSONDecodeError as e: | |||
return Response(json.dumps({"data": f"Error decoding JSON: {e}"}), status=401, mimetype='application/json') | |||
else: | |||
return Response(json.dumps({"data": response.text}), status=401, mimetype='application/json') | |||
@xhiveframework.whitelist() | |||
def customer(customer, customer_type, phone, email,is_supplier=False): | |||
response_content =xhiveframework.session.user | |||
is_supplier = is_supplier.lower() in ['true', '1', 'yes'] if isinstance(is_supplier, str) else bool(is_supplier) | |||
if not is_supplier: | |||
email_result = [] | |||
if email is not None: | |||
email_result = xhiveframework.get_all("Customer", fields=["name as id", "customer_name", "customer_type", "mobile_no", "custom_email as email"], | |||
filters={'custom_email': ['like', email]}) | |||
if not email_result: | |||
customer_doc = xhiveframework.get_doc({ | |||
"doctype": "Customer", | |||
"customer_name": customer, | |||
"customer_type": customer_type, | |||
"mobile_no": phone, | |||
"custom_email": email, | |||
}) | |||
customer_doc.insert(ignore_permissions=True) | |||
customer_doc.save() | |||
details = xhiveframework.get_all("Customer", fields=["name as id", "customer_name", "customer_type", "mobile_no", "custom_email as email",], | |||
filters={'name': ['like', customer_doc.name]}) | |||
data = { | |||
"message": "Customer created successfully.", | |||
"Details": details, | |||
} | |||
return Response(json.dumps({"data":data}), status=200, mimetype='application/json') | |||
else: | |||
data = { | |||
"message": "Customer already exists", | |||
"Details": email_result | |||
} | |||
return Response(json.dumps({"data":data}), status=200, mimetype='application/json') | |||
else: | |||
suplr_already_exist=xhiveframework.get_all("Supplier", fields=["name as id", "supplier_name", "custom_email as email", "custom_mobileno as phone"], | |||
filters={'name': ['like',customer]}) | |||
details={ | |||
"message":"Already exists", | |||
"Details":suplr_already_exist | |||
} | |||
if suplr_already_exist: | |||
return Response(json.dumps({"data":details }), status=409, mimetype='application/json') | |||
supplier_doc = xhiveframework.get_doc({ | |||
"doctype": "Supplier", | |||
"supplier_name": customer, | |||
"custom_mobileno": phone, | |||
"custom_email": email, | |||
}) | |||
supplier_doc.insert(ignore_permissions=True) | |||
supplier_details = xhiveframework.get_all("Supplier", fields=["name as id", "supplier_name", "custom_email as email", "custom_mobileno as phone"], | |||
filters={'name': ['like', supplier_doc.name]}) | |||
suplr={ | |||
"Details":supplier_details | |||
} | |||
return Response(json.dumps({"data":suplr}), status=200, mimetype='application/json') | |||
@xhiveframework.whitelist() | |||
def create_invoice(customer_id, supplier_id, payment_method, items): | |||
invoice_items = [] | |||
for item in items: | |||
item_code = item["item_name"] | |||
item_exists = xhiveframework.get_value("Item", {"name": item_code}, "name") | |||
if not item_exists: | |||
invoice_item = { | |||
"item_name": item_code, | |||
"qty": item.get("quantity", 0), | |||
"rate": item.get("rate", 0), | |||
"uom": item.get("uom", "Nos"), | |||
"income_account": item.get("income_account", "Sales - erp") | |||
} | |||
else: | |||
invoice_item = { | |||
"item_code": item_code, | |||
"qty": item.get("quantity", 0), | |||
"rate": item.get("rate", 0), | |||
} | |||
invoice_items.append(invoice_item) | |||
new_invoice = xhiveframework.get_doc({ | |||
"doctype": "Sales Invoice", | |||
"customer": customer_id, | |||
"custom_supplier_id": supplier_id, | |||
"custom_payment_method": payment_method, | |||
"items": invoice_items | |||
}) | |||
new_invoice.insert(ignore_permissions=True) | |||
new_invoice.save() | |||
iitem = xhiveframework.get_doc("Sales Invoice", new_invoice.name) | |||
attribute_dict = [] | |||
for attribute in iitem.items: | |||
attribute_data = { | |||
"item_name": attribute.item_name, | |||
"item_code": attribute.item_code, | |||
"quantity": attribute.qty, | |||
"rate": attribute.rate, | |||
"uom": attribute.uom, | |||
"income_account": attribute.income_account | |||
} | |||
attribute_dict.append(attribute_data) | |||
customer_info = { | |||
"id": new_invoice.name, | |||
"customer_id": new_invoice.customer, | |||
"customer_name": new_invoice.customer_name, | |||
"supplier_id": new_invoice.custom_supplier_id, | |||
"payment_method": new_invoice.custom_payment_method, | |||
"total_quantity": new_invoice.total_qty, | |||
"total": new_invoice.total, | |||
"grand_total": new_invoice.grand_total, | |||
"items": attribute_dict | |||
} | |||
return Response(json.dumps({"data":customer_info}), status=200, mimetype='application/json') | |||
@xhiveframework.whitelist() | |||
def customer1(customer, phone, email, is_supplier=False, user_id=None): | |||
try: | |||
return "hello" | |||
except xhiveframework.exceptions.PermissionError as e: | |||
return Response(json.dumps({"message": "Permission error"}), status=401, mimetype='application/json') | |||
except Exception as e: | |||
# Handle other exceptions if needed | |||
return Response(json.dumps({"message": str(e)}), status=500, mimetype='application/json') |
@@ -0,0 +1 @@ | |||
Invoice Sync |
@@ -0,0 +1,6 @@ | |||
[pre_model_sync] | |||
# Patches added in this section will be executed before doctypes are migrated | |||
# Read docs to understand patches: https://xhiveframework.com/docs/v14/user/en/database-migrations | |||
[post_model_sync] | |||
# Patches added in this section will be executed after doctypes are migrated |
@@ -0,0 +1,21 @@ | |||
MIT License | |||
Copyright (c) [year] [fullname] | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. |
@@ -0,0 +1,20 @@ | |||
[project] | |||
name = "invoice_sync" | |||
authors = [ | |||
{ name = "SolutionERP", email = "support@solutionerp.com"} | |||
] | |||
description = "Syncing invoice with mobile app" | |||
requires-python = ">=3.10" | |||
readme = "README.md" | |||
dynamic = ["version"] | |||
dependencies = [ | |||
# "xhiveframework~=15.0.0" # Installed and managed by bench. | |||
] | |||
[build-system] | |||
requires = ["flit_core >=3.4,<4"] | |||
build-backend = "flit_core.buildapi" | |||
# These dependencies are only installed when developer mode is enabled | |||
[tool.bench.dev-dependencies] | |||
# package_name = "~=1.1.0" |