@@ -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" |