diff --git a/erpnext/erpnext_integrations/doctype/flowkana_settings/__init__.py b/erpnext/erpnext_integrations/doctype/flowkana_settings/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/erpnext_integrations/doctype/flowkana_settings/flowkana_settings.js b/erpnext/erpnext_integrations/doctype/flowkana_settings/flowkana_settings.js new file mode 100644 index 000000000000..70d47f744989 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/flowkana_settings/flowkana_settings.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Flowkana Settings', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/erpnext_integrations/doctype/flowkana_settings/flowkana_settings.json b/erpnext/erpnext_integrations/doctype/flowkana_settings/flowkana_settings.json new file mode 100644 index 000000000000..91c6002e78d2 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/flowkana_settings/flowkana_settings.json @@ -0,0 +1,74 @@ +{ + "creation": "2021-03-19 03:53:40.417764", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "enable_flowkana_section", + "enable_flowkana", + "api_key", + "api_value", + "load_url_in_new_tab", + "url_tab" + ], + "fields": [ + { + "fieldname": "enable_flowkana_section", + "fieldtype": "Section Break", + "label": "Enable Flowkana" + }, + { + "default": "0", + "fieldname": "enable_flowkana", + "fieldtype": "Check", + "label": "Enable Flowkana" + }, + { + "depends_on": "eval: doc.enable_flowkana == 1", + "fieldname": "api_key", + "fieldtype": "Data", + "label": "API Key" + }, + { + "depends_on": "eval: doc.enable_flowkana == 1", + "fieldname": "api_value", + "fieldtype": "Data", + "label": "API Value" + }, + { + "default": "0", + "depends_on": "eval: doc.enable_flowkana == 1", + "fieldname": "load_url_in_new_tab", + "fieldtype": "Check", + "label": "Load URL in new tab" + }, + { + "depends_on": "eval: doc.enable_flowkana == 1", + "fieldname": "url_tab", + "fieldtype": "Data", + "label": "URL Tab" + } + ], + "issingle": 1, + "modified": "2021-03-19 04:07:51.319530", + "modified_by": "Administrator", + "module": "ERPNext Integrations", + "name": "Flowkana Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/flowkana_settings/flowkana_settings.py b/erpnext/erpnext_integrations/doctype/flowkana_settings/flowkana_settings.py new file mode 100644 index 000000000000..c0176b319e91 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/flowkana_settings/flowkana_settings.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import datetime +import requests +import json +from six import text_type +from frappe.model.document import Document + +class FlowkanaSettings(Document): + + def validate(self): + self.check_fulfillment_partner() + self.toggle_status() + + def toggle_status(self): + frappe.db.set_value("Fulfillment Partner", "Flowkana", "enable_flowkana", self.enable_flowkana) + + def check_fulfillment_partner(self): + """ + Create a fulfillment partner in case they don't exist. + """ + if self.enable_flowkana and not frappe.db.exists("Fulfillment Partner", "Flowkana"): + partner = frappe.get_doc({ + "doctype": "Fulfillment Partner", + "partner_name": "Flowkana", + "enable": 1 + }).save() + +def send_delivery_request_to_flowkana(sales_order): + """ + Ping flowkana with sales order details and map response to an integration request. + + Args: + sales_order_name (string): name of the sales order that needs to be sent to flowkana + """ + #create line items + item_list = [] + for item in sales_order.items: + ivt_id = frappe.get_value("Item", item.item_code, "ivt_id") + line_item = { + "attributes": { + "ivt_id": ivt_id, + "test_batch_id": item.batch_no, + "external_item_code": item.item_code, + "unit_quantity": item.qty, + "unit_price": item.rate + } + } + item_list.append(line_item) + + #prepare response json + request_json = { + "data": { + "attributes": { + "external_order_id": sales_order.name, + "customer_name": sales_order.customer, + "customer_license": sales_order.license, + "delivery_date": str(sales_order.delivery_date), + "note": "Test Note" + }, + "relationships": { + "order_line_items": item_list + } + } + } + + #create integration request + integration_request = frappe.new_doc("Integration Request") + integration_request.update({ + "integration_type": "Host", + "integration_request_service": "Flowkana", + "status": "Queued", + "data": json.dumps(request_json, default=json_handler), + "reference_doctype": "Sales Order", + "reference_docname": sales_order.name + }) + integration_request.save(ignore_permissions=True) + + #fetch and prepare headers from flowkana settings, flag error if missing data + flowkana_settings = frappe.get_cached_doc("Flowkana Settings") + if not flowkana_settings.get("url_tab"): + frappe.throw(_("Please provide an endpoint to send data to.")) + if not flowkana_settings.get("api_key", 0.0): + frappe.throw(_("Please enter API Key in flowkana settings.")) + if not flowkana_settings.get("api_value", 0.0): + frappe.throw(_("Please enter API Value in flowkana settings.")) + + headers = { + flowkana_settings.get("api_key"): flowkana_settings.get("api_value") + } + + print("request_json: ", request_json) + + #ping flowkana with requisite headers and data + response = requests.post( + flowkana_settings.get("url_tab"), + headers=headers, + json=request_json) + response_data = response.json() + + if response.status_code not in [200, 201, 202]: + frappe.throw("The response has the following errors: {0}".format(response_data.get("errors"),"")) + return + + #mark integration request status as queued, update status to queued + integration_request.output = json.dumps(response_data, default=json_handler) + integration_request.save(ignore_permissions=True) + +def json_handler(obj): + if isinstance(obj, (datetime.date, datetime.timedelta, datetime.datetime)): + return text_type(obj) diff --git a/erpnext/erpnext_integrations/doctype/flowkana_settings/test_flowkana_settings.py b/erpnext/erpnext_integrations/doctype/flowkana_settings/test_flowkana_settings.py new file mode 100644 index 000000000000..45e403b13132 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/flowkana_settings/test_flowkana_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestFlowkanaSettings(unittest.TestCase): + pass diff --git a/erpnext/patches.txt b/erpnext/patches.txt index a50a2d0684c6..bec15bd8d779 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -715,7 +715,8 @@ erpnext.patches.v13_0.delete_package_tag_property_setter_search_fields erpnext.patches.v13_0.set_batch_qty_in_batch erpnext.patches.v13_0.set_stock_valuation_method #2021-03-15 erpnext.patches.v13_0.delete_bank_reconciliation_doctype -erpnext.patches.v13_0.make_mobile_application_access_roles +erpnext.patches.v13_0.make_mobile_application_access_roles #2021-04-08 execute:frappe.reload_doc('setup', 'doctype', 'company', force=True) erpnext.patches.v13_0.set_is_used #01-04-2021 erpnext.patches.v13_0.set_coa_batch_in_batch +erpnext.patches.v13_0.delete_company_custom_fields diff --git a/erpnext/patches/v13_0/delete_company_custom_fields.py b/erpnext/patches/v13_0/delete_company_custom_fields.py new file mode 100644 index 000000000000..b0622e75d978 --- /dev/null +++ b/erpnext/patches/v13_0/delete_company_custom_fields.py @@ -0,0 +1,7 @@ +import frappe + +def execute(): + custom_fields = ["column_break_59", "section_break_56", "licenses_sb", "licenses"] + + for field in custom_fields: + frappe.delete_doc_if_exists("Custom Field", field) \ No newline at end of file diff --git a/erpnext/patches/v13_0/make_mobile_application_access_roles.py b/erpnext/patches/v13_0/make_mobile_application_access_roles.py index 08fafdfdcada..e6ec000d7b2c 100644 --- a/erpnext/patches/v13_0/make_mobile_application_access_roles.py +++ b/erpnext/patches/v13_0/make_mobile_application_access_roles.py @@ -2,19 +2,13 @@ def execute(): - roles = [ - { - "role_name": "Expense Module", - "for_mobile_application": 1 - }, - { - "role_name": "Driver Module", - "for_mobile_application": 1 - } - ] + roles = ["Expense Module", "Driver Module"] for role in roles: - frappe.get_doc({ - "doctype": "Role", - "role_name": role.get("role_name"), - "for_mobile_application": role.get("for_mobile_application") - }).insert(ignore_permissions=True) \ No newline at end of file + if frappe.db.exists("Role", role): + frappe.db.set_value("Role", role, "for_mobile_application", 1) + else: + frappe.get_doc({ + "doctype": "Role", + "role_name": role, + "for_mobile_application": 1, + }).insert(ignore_permissions=True) \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index a93c0d8f1ca5..53a7f32e5653 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -21,6 +21,7 @@ "company", "transaction_date", "delivery_date", + "fulfillment_partner", "po_no", "po_date", "tax_id", @@ -1257,12 +1258,18 @@ "fieldtype": "Check", "label": "Advance Received", "read_only": 1 + }, + { + "fieldname": "fulfillment_partner", + "fieldtype": "Link", + "label": "Fulfillment Partner", + "options": "Fulfillment Partner" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, - "modified": "2021-01-14 03:20:54.326989", + "modified": "2021-03-19 04:08:31.995032", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 7d6447a7a4d2..764a87d956e2 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -5,6 +5,7 @@ import frappe import json import calendar +import requests import frappe.utils from frappe.utils import cstr, flt, getdate, cint, nowdate, add_days, get_link_to_form, comma_and from frappe import _ @@ -14,10 +15,12 @@ from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty from frappe.desk.notifications import clear_doctype_notifications from frappe.contacts.doctype.address.address import get_company_address +from frappe.integrations.utils import create_payment_gateway, create_request_log from erpnext.controllers.selling_controller import SellingController from frappe.automation.doctype.auto_repeat.auto_repeat import get_next_schedule_date from erpnext.selling.doctype.customer.customer import check_credit_limit from erpnext.stock.doctype.item.item import get_item_defaults +from erpnext.erpnext_integrations.doctype.flowkana_settings.flowkana_settings import send_delivery_request_to_flowkana from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\ @@ -189,6 +192,13 @@ def on_submit(self): from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count update_coupon_code_count(self.coupon_code,'used') + flowkana_settings = frappe.get_cached_doc("Flowkana Settings") + + #send delivery request to flowkana if enabled by user + if flowkana_settings.enable_flowkana and self.fulfillment_partner == "Flowkana": + send_delivery_request_to_flowkana(self) + + def on_cancel(self): super(SalesOrder, self).on_cancel() diff --git a/erpnext/stock/doctype/fulfillment_partner/__init__.py b/erpnext/stock/doctype/fulfillment_partner/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/stock/doctype/fulfillment_partner/fulfillment_partner.js b/erpnext/stock/doctype/fulfillment_partner/fulfillment_partner.js new file mode 100644 index 000000000000..47f0eed5999b --- /dev/null +++ b/erpnext/stock/doctype/fulfillment_partner/fulfillment_partner.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Fulfillment Partner', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/stock/doctype/fulfillment_partner/fulfillment_partner.json b/erpnext/stock/doctype/fulfillment_partner/fulfillment_partner.json new file mode 100644 index 000000000000..a0a37038250f --- /dev/null +++ b/erpnext/stock/doctype/fulfillment_partner/fulfillment_partner.json @@ -0,0 +1,48 @@ +{ + "autoname": "field:partner_name", + "creation": "2021-03-19 04:02:49.718866", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "partner_name", + "enable" + ], + "fields": [ + { + "default": "0", + "fieldname": "enable", + "fieldtype": "Check", + "label": "Enable" + }, + { + "fieldname": "partner_name", + "fieldtype": "Data", + "label": "Partner Name", + "unique": 1 + } + ], + "modified": "2021-03-19 04:05:27.925753", + "modified_by": "Administrator", + "module": "Stock", + "name": "Fulfillment Partner", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/stock/doctype/fulfillment_partner/fulfillment_partner.py b/erpnext/stock/doctype/fulfillment_partner/fulfillment_partner.py new file mode 100644 index 000000000000..f11e53e2ee25 --- /dev/null +++ b/erpnext/stock/doctype/fulfillment_partner/fulfillment_partner.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class FulfillmentPartner(Document): + pass diff --git a/erpnext/stock/doctype/fulfillment_partner/test_fulfillment_partner.py b/erpnext/stock/doctype/fulfillment_partner/test_fulfillment_partner.py new file mode 100644 index 000000000000..e013fed98960 --- /dev/null +++ b/erpnext/stock/doctype/fulfillment_partner/test_fulfillment_partner.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestFulfillmentPartner(unittest.TestCase): + pass diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 92f4ce412999..aa4a089c0a8e 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -146,7 +146,9 @@ "publish_in_hub", "hub_category_to_publish", "hub_warehouse", - "synced_with_hub" + "synced_with_hub", + "flowkana_details_section", + "ivt_id" ], "fields": [ { @@ -1115,6 +1117,16 @@ "fieldtype": "Table", "label": "Shipping Information", "options": "Shipping Information" + }, + { + "fieldname": "flowkana_details_section", + "fieldtype": "Section Break", + "label": "Flowkana Details" + }, + { + "fieldname": "ivt_id", + "fieldtype": "Data", + "label": "Inventory ID" } ], "has_web_view": 1, @@ -1122,7 +1134,7 @@ "idx": 2, "image_field": "image", "max_attachments": 1, - "modified": "2021-02-02 00:19:02.360290", + "modified": "2021-03-30 00:55:05.858803", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/www/lms/course.html b/erpnext/www/lms/course.html index 63d020161eb6..34b1e3987b43 100644 --- a/erpnext/www/lms/course.html +++ b/erpnext/www/lms/course.html @@ -120,10 +120,12 @@
{{total_progress}}% Completed
{{total_progress}}% Completed
{{total_progress}}% Completed