Merge branch 'develop' into patch-1

This commit is contained in:
Suraj Shetty
2020-06-22 21:41:31 +05:30
committed by GitHub
504 changed files with 9747 additions and 7492 deletions

14
.github/workflows/docker-release.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
name: Trigger Docker build on release
on:
release:
types: [released]
jobs:
curl:
runs-on: ubuntu-latest
container:
image: alpine:latest
steps:
- name: curl
run: |
apk add curl bash
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.com/repo/frappe%2Ffrappe_docker/requests

View File

@@ -3,17 +3,16 @@
# These owners will be the default owners for everything in # These owners will be the default owners for everything in
# the repo. Unless a later match takes precedence, # the repo. Unless a later match takes precedence,
* @nabinhait manufacturing/ @rohitwaghchaure @marination
manufacturing/ @rohitwaghchaure
accounts/ @deepeshgarg007 @nextchamp-saqib accounts/ @deepeshgarg007 @nextchamp-saqib
loan_management/ @deepeshgarg007 loan_management/ @deepeshgarg007 @rohitwaghchaure
pos* @nextchamp-saqib pos* @nextchamp-saqib @rohitwaghchaure
assets/ @nextchamp-saqib assets/ @nextchamp-saqib @deepeshgarg007
stock/ @marination @rohitwaghchaure stock/ @marination @rohitwaghchaure
buying/ @marination @rohitwaghchaure buying/ @marination @deepeshgarg007
hr/ @Anurag810 hr/ @Anurag810 @rohitwaghchaure
projects/ @hrwX projects/ @hrwX @nextchamp-saqib
support/ @hrwX support/ @hrwX @marination
healthcare/ @ruchamahabal healthcare/ @ruchamahabal @marination
erpnext_integrations/ @Mangesh-Khairnar erpnext_integrations/ @Mangesh-Khairnar @nextchamp-saqib
requirements.txt @gavindsouza requirements.txt @gavindsouza

View File

@@ -5,7 +5,22 @@ import frappe
import json import json
from frappe.utils import nowdate, add_months, get_date_str from frappe.utils import nowdate, add_months, get_date_str
from frappe import _ from frappe import _
from erpnext.accounts.utils import get_fiscal_year, get_account_name from erpnext.accounts.utils import get_fiscal_year, get_account_name, FiscalYearError
def _get_fiscal_year(date=None):
try:
fiscal_year = get_fiscal_year(date=nowdate(), as_dict=True)
return fiscal_year
except FiscalYearError:
#if no fiscal year for current date then get default fiscal year
try:
fiscal_year = get_fiscal_year(as_dict=True)
return fiscal_year
except FiscalYearError:
#if still no fiscal year found then no accounting data created, return
return None
def get_company_for_dashboards(): def get_company_for_dashboards():
company = frappe.defaults.get_defaults().company company = frappe.defaults.get_defaults().company
@@ -18,10 +33,16 @@ def get_company_for_dashboards():
return None return None
def get_data(): def get_data():
fiscal_year = _get_fiscal_year(nowdate())
if not fiscal_year:
return frappe._dict()
return frappe._dict({ return frappe._dict({
"dashboards": get_dashboards(), "dashboards": get_dashboards(),
"charts": get_charts(), "charts": get_charts(fiscal_year),
"number_cards": get_number_cards() "number_cards": get_number_cards(fiscal_year)
}) })
def get_dashboards(): def get_dashboards():
@@ -46,10 +67,9 @@ def get_dashboards():
] ]
}] }]
def get_charts(): def get_charts(fiscal_year):
company = frappe.get_doc("Company", get_company_for_dashboards()) company = frappe.get_doc("Company", get_company_for_dashboards())
bank_account = company.default_bank_account or get_account_name("Bank", company=company.name) bank_account = company.default_bank_account or get_account_name("Bank", company=company.name)
fiscal_year = get_fiscal_year(date=nowdate())
default_cost_center = company.cost_center default_cost_center = company.cost_center
return [ return [
@@ -60,9 +80,9 @@ def get_charts():
"report_name": "Profit and Loss Statement", "report_name": "Profit and Loss Statement",
"filters_json": json.dumps({ "filters_json": json.dumps({
"company": company.name, "company": company.name,
"filter_based_on": "Date Range", "filter_based_on": "Fiscal Year",
"period_start_date": get_date_str(fiscal_year[1]), "from_fiscal_year": fiscal_year.get('name'),
"period_end_date": get_date_str(fiscal_year[2]), "to_fiscal_year": fiscal_year.get('name'),
"periodicity": "Monthly", "periodicity": "Monthly",
"include_default_book_entries": 1 "include_default_book_entries": 1
}), }),
@@ -158,8 +178,8 @@ def get_charts():
"report_name": "Budget Variance Report", "report_name": "Budget Variance Report",
"filters_json": json.dumps({ "filters_json": json.dumps({
"company": company.name, "company": company.name,
"from_fiscal_year": fiscal_year[0], "from_fiscal_year": fiscal_year.get('name'),
"to_fiscal_year": fiscal_year[0], "to_fiscal_year": fiscal_year.get('name'),
"period": "Monthly", "period": "Monthly",
"budget_against": "Cost Center" "budget_against": "Cost Center"
}), }),
@@ -190,10 +210,10 @@ def get_charts():
}, },
] ]
def get_number_cards(): def get_number_cards(fiscal_year):
fiscal_year = get_fiscal_year(date=nowdate())
year_start_date = get_date_str(fiscal_year[1]) year_start_date = get_date_str(fiscal_year.get("year_start_date"))
year_end_date = get_date_str(fiscal_year[2]) year_end_date = get_date_str(fiscal_year.get("year_end_date"))
return [ return [
{ {
"doctype": "Number Card", "doctype": "Number Card",

View File

@@ -13,7 +13,7 @@
{ {
"hidden": 0, "hidden": 0,
"label": "Accounts Receivable", "label": "Accounts Receivable",
"links": "[\n {\n \"description\": \"Bills raised to Customers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Request\",\n \"label\": \"Payment Request\",\n \"name\": \"Payment Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable\",\n \"name\": \"Accounts Receivable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable Summary\",\n \"name\": \"Accounts Receivable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Register\",\n \"name\": \"Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales Register\",\n \"name\": \"Item-wise Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Billed\",\n \"name\": \"Ordered Items To Be Billed\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Delivered Items To Be Billed\",\n \"name\": \"Delivered Items To Be Billed\",\n \"type\": \"report\"\n }\n]" "links": "[\n {\n \"description\": \"Bills raised to Customers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Request\",\n \"label\": \"Payment Request\",\n \"name\": \"Payment Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable\",\n \"name\": \"Accounts Receivable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable Summary\",\n \"name\": \"Accounts Receivable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Register\",\n \"name\": \"Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales Register\",\n \"name\": \"Item-wise Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Delivered Items To Be Billed\",\n \"name\": \"Delivered Items To Be Billed\",\n \"type\": \"report\"\n }\n]"
}, },
{ {
"hidden": 0, "hidden": 0,
@@ -98,7 +98,7 @@
"idx": 0, "idx": 0,
"is_standard": 1, "is_standard": 1,
"label": "Accounting", "label": "Accounting",
"modified": "2020-05-27 20:34:50.949772", "modified": "2020-06-19 12:42:44.054598",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounting", "name": "Accounting",
@@ -122,11 +122,6 @@
"link_to": "Purchase Invoice", "link_to": "Purchase Invoice",
"type": "DocType" "type": "DocType"
}, },
{
"label": "Dashboard",
"link_to": "Accounts",
"type": "Dashboard"
},
{ {
"label": "Journal Entry", "label": "Journal Entry",
"link_to": "Journal Entry", "link_to": "Journal Entry",
@@ -151,6 +146,11 @@
"label": "Trial Balance", "label": "Trial Balance",
"link_to": "Trial Balance", "link_to": "Trial Balance",
"type": "Report" "type": "Report"
},
{
"label": "Dashboard",
"link_to": "Accounts",
"type": "Dashboard"
} }
] ]
} }

View File

@@ -14,6 +14,9 @@ frappe.treeview_settings["Account"] = {
on_change: function() { on_change: function() {
var me = frappe.treeview_settings['Account'].treeview; var me = frappe.treeview_settings['Account'].treeview;
var company = me.page.fields_dict.company.get_value(); var company = me.page.fields_dict.company.get_value();
if (!company) {
frappe.throw(__("Please set a Company"));
}
frappe.call({ frappe.call({
method: "erpnext.accounts.doctype.account.account.get_root_company", method: "erpnext.accounts.doctype.account.account.get_root_company",
args: { args: {

View File

@@ -72,6 +72,10 @@ def make_dimension_in_accounting_doctypes(doc):
if doctype == "Budget": if doctype == "Budget":
add_dimension_to_budget_doctype(df, doc) add_dimension_to_budget_doctype(df, doc)
else: else:
meta = frappe.get_meta(doctype, cached=False)
fieldnames = [d.fieldname for d in meta.get("fields")]
if df['fieldname'] not in fieldnames:
create_custom_field(doctype, df) create_custom_field(doctype, df)
count += 1 count += 1

View File

@@ -14,7 +14,18 @@ frappe.ui.form.on('Cost Center', {
is_group: 1 is_group: 1
} }
} }
}) });
frm.set_query("cost_center", "distributed_cost_center", function() {
return {
filters: {
company: frm.doc.company,
is_group: 0,
enable_distributed_cost_center: 0,
name: ['!=', frm.doc.name]
}
};
});
}, },
refresh: function(frm) { refresh: function(frm) {
if (!frm.is_new()) { if (!frm.is_new()) {
@@ -60,8 +71,13 @@ frappe.ui.form.on('Cost Center', {
"label": "Cost Center Number", "label": "Cost Center Number",
"fieldname": "cost_center_number", "fieldname": "cost_center_number",
"fieldtype": "Data", "fieldtype": "Data",
"reqd": 1,
"default": frm.doc.cost_center_number "default": frm.doc.cost_center_number
},
{
"label": __("Merge with existing"),
"fieldname": "merge",
"fieldtype": "Check",
"default": 0
} }
], ],
primary_action: function() { primary_action: function() {
@@ -76,8 +92,9 @@ frappe.ui.form.on('Cost Center', {
args: { args: {
docname: frm.doc.name, docname: frm.doc.name,
cost_center_name: data.cost_center_name, cost_center_name: data.cost_center_name,
cost_center_number: data.cost_center_number, cost_center_number: cstr(data.cost_center_number),
company: frm.doc.company company: frm.doc.company,
merge: data.merge
}, },
callback: function(r) { callback: function(r) {
frappe.dom.unfreeze(); frappe.dom.unfreeze();

View File

@@ -16,6 +16,9 @@
"cb0", "cb0",
"is_group", "is_group",
"disabled", "disabled",
"section_break_9",
"enable_distributed_cost_center",
"distributed_cost_center",
"lft", "lft",
"rgt", "rgt",
"old_parent" "old_parent"
@@ -119,13 +122,31 @@
"fieldname": "disabled", "fieldname": "disabled",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Disabled" "label": "Disabled"
},
{
"default": "0",
"fieldname": "enable_distributed_cost_center",
"fieldtype": "Check",
"label": "Enable Distributed Cost Center"
},
{
"depends_on": "eval:doc.is_group==0",
"fieldname": "section_break_9",
"fieldtype": "Section Break"
},
{
"depends_on": "enable_distributed_cost_center",
"fieldname": "distributed_cost_center",
"fieldtype": "Table",
"label": "Distributed Cost Center",
"options": "Distributed Cost Center"
} }
], ],
"icon": "fa fa-money", "icon": "fa fa-money",
"idx": 1, "idx": 1,
"is_tree": 1, "is_tree": 1,
"links": [], "links": [],
"modified": "2020-04-29 16:09:30.025214", "modified": "2020-06-17 16:09:30.025214",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Cost Center", "name": "Cost Center",

View File

@@ -19,6 +19,24 @@ class CostCenter(NestedSet):
def validate(self): def validate(self):
self.validate_mandatory() self.validate_mandatory()
self.validate_parent_cost_center() self.validate_parent_cost_center()
self.validate_distributed_cost_center()
def validate_distributed_cost_center(self):
if cint(self.enable_distributed_cost_center):
if not self.distributed_cost_center:
frappe.throw(_("Please enter distributed cost center"))
if sum(x.percentage_allocation for x in self.distributed_cost_center) != 100:
frappe.throw(_("Total percentage allocation for distributed cost center should be equal to 100"))
if not self.get('__islocal'):
if not cint(frappe.get_cached_value("Cost Center", {"name": self.name}, "enable_distributed_cost_center")) \
and self.check_if_part_of_distributed_cost_center():
frappe.throw(_("Cannot enable Distributed Cost Center for a Cost Center already allocated in another Distributed Cost Center"))
if next((True for x in self.distributed_cost_center if x.cost_center == x.parent), False):
frappe.throw(_("Parent Cost Center cannot be added in Distributed Cost Center"))
if check_if_distributed_cost_center_enabled(list(x.cost_center for x in self.distributed_cost_center)):
frappe.throw(_("A Distributed Cost Center cannot be added in the Distributed Cost Center allocation table."))
else:
self.distributed_cost_center = []
def validate_mandatory(self): def validate_mandatory(self):
if self.cost_center_name != self.company and not self.parent_cost_center: if self.cost_center_name != self.company and not self.parent_cost_center:
@@ -43,9 +61,12 @@ class CostCenter(NestedSet):
return 1 return 1
def convert_ledger_to_group(self): def convert_ledger_to_group(self):
if cint(self.enable_distributed_cost_center):
frappe.throw(_("Cost Center with enabled distributed cost center can not be converted to group"))
if self.check_if_part_of_distributed_cost_center():
frappe.throw(_("Cost Center Already Allocated in a Distributed Cost Center cannot be converted to group"))
if self.check_gle_exists(): if self.check_gle_exists():
frappe.throw(_("Cost Center with existing transactions can not be converted to group")) frappe.throw(_("Cost Center with existing transactions can not be converted to group"))
else:
self.is_group = 1 self.is_group = 1
self.save() self.save()
return 1 return 1
@@ -57,6 +78,9 @@ class CostCenter(NestedSet):
return frappe.db.sql("select name from `tabCost Center` where \ return frappe.db.sql("select name from `tabCost Center` where \
parent_cost_center = %s and docstatus != 2", self.name) parent_cost_center = %s and docstatus != 2", self.name)
def check_if_part_of_distributed_cost_center(self):
return frappe.db.get_value("Distributed Cost Center", {"cost_center": self.name})
def before_rename(self, olddn, newdn, merge=False): def before_rename(self, olddn, newdn, merge=False):
# Add company abbr if not provided # Add company abbr if not provided
from erpnext.setup.doctype.company.company import get_name_with_abbr from erpnext.setup.doctype.company.company import get_name_with_abbr
@@ -100,3 +124,7 @@ def get_name_with_number(new_account, account_number):
if account_number and not new_account[0].isdigit(): if account_number and not new_account[0].isdigit():
new_account = account_number + " - " + new_account new_account = account_number + " - " + new_account
return new_account return new_account
def check_if_distributed_cost_center_enabled(cost_center_list):
value_list = frappe.get_list("Cost Center", {"name": ["in", cost_center_list]}, "enable_distributed_cost_center", as_list=1)
return next((True for x in value_list if x[0]), False)

View File

@@ -22,6 +22,33 @@ class TestCostCenter(unittest.TestCase):
self.assertRaises(frappe.ValidationError, cost_center.save) self.assertRaises(frappe.ValidationError, cost_center.save)
def test_validate_distributed_cost_center(self):
if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center - _TC'}):
frappe.get_doc(test_records[0]).insert()
if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center 2 - _TC'}):
frappe.get_doc(test_records[1]).insert()
invalid_distributed_cost_center = frappe.get_doc({
"company": "_Test Company",
"cost_center_name": "_Test Distributed Cost Center",
"doctype": "Cost Center",
"is_group": 0,
"parent_cost_center": "_Test Company - _TC",
"enable_distributed_cost_center": 1,
"distributed_cost_center": [{
"cost_center": "_Test Cost Center - _TC",
"percentage_allocation": 40
}, {
"cost_center": "_Test Cost Center 2 - _TC",
"percentage_allocation": 50
}
]
})
self.assertRaises(frappe.ValidationError, invalid_distributed_cost_center.save)
def create_cost_center(**args): def create_cost_center(**args):
args = frappe._dict(args) args = frappe._dict(args)
if args.cost_center_name: if args.cost_center_name:

View File

@@ -0,0 +1,40 @@
{
"actions": [],
"creation": "2020-03-19 12:34:01.500390",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"cost_center",
"percentage_allocation"
],
"fields": [
{
"fieldname": "cost_center",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Cost Center",
"options": "Cost Center",
"reqd": 1
},
{
"fieldname": "percentage_allocation",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Percentage Allocation",
"reqd": 1
}
],
"istable": 1,
"links": [],
"modified": "2020-03-19 12:54:43.674655",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Distributed Cost Center",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, 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 DistributedCostCenter(Document):
pass

View File

@@ -4,7 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, erpnext import frappe, erpnext
from frappe import _ from frappe import _
from frappe.utils import flt, fmt_money, getdate, formatdate from frappe.utils import flt, fmt_money, getdate, formatdate, cint
from frappe.model.document import Document from frappe.model.document import Document
from frappe.model.naming import set_name_from_naming_options from frappe.model.naming import set_name_from_naming_options
from frappe.model.meta import get_field_precision from frappe.model.meta import get_field_precision
@@ -134,10 +134,17 @@ class GLEntry(Document):
return self.cost_center_company[self.cost_center] return self.cost_center_company[self.cost_center]
def _check_is_group():
return cint(frappe.get_cached_value('Cost Center', self.cost_center, 'is_group'))
if self.cost_center and _get_cost_center_company() != self.company: if self.cost_center and _get_cost_center_company() != self.company:
frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}") frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}")
.format(self.voucher_type, self.voucher_no, self.cost_center, self.company)) .format(self.voucher_type, self.voucher_no, self.cost_center, self.company))
if self.cost_center and _check_is_group():
frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot
be used in transactions""").format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
def validate_party(self): def validate_party(self):
validate_party_frozen_disabled(self.party_type, self.party) validate_party_frozen_disabled(self.party_type, self.party)

View File

@@ -278,7 +278,7 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
// payroll entry // payroll entry
if(jvd.reference_type==="Payroll Entry") { if(jvd.reference_type==="Payroll Entry") {
return { return {
query: "erpnext.hr.doctype.payroll_entry.payroll_entry.get_payroll_entries_for_jv", query: "erpnext.payroll.doctype.payroll_entry.payroll_entry.get_payroll_entries_for_jv",
}; };
} }

View File

@@ -191,6 +191,7 @@
{ {
"fieldname": "total_debit", "fieldname": "total_debit",
"fieldtype": "Currency", "fieldtype": "Currency",
"in_list_view": 1,
"label": "Total Debit", "label": "Total Debit",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "total_debit", "oldfieldname": "total_debit",
@@ -252,7 +253,6 @@
"fieldname": "total_amount", "fieldname": "total_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 1, "hidden": 1,
"in_list_view": 1,
"label": "Total Amount", "label": "Total Amount",
"no_copy": 1, "no_copy": 1,
"options": "total_amount_currency", "options": "total_amount_currency",
@@ -503,7 +503,7 @@
"idx": 176, "idx": 176,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-04-29 10:55:28.240916", "modified": "2020-06-02 18:15:46.955697",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry", "name": "Journal Entry",

View File

@@ -54,7 +54,7 @@ class JournalEntry(AccountsController):
def on_cancel(self): def on_cancel(self):
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip from erpnext.payroll.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
unlink_ref_doc_from_payment_entries(self) unlink_ref_doc_from_payment_entries(self)
unlink_ref_doc_from_salary_slip(self.name) unlink_ref_doc_from_salary_slip(self.name)
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')

View File

@@ -18,6 +18,7 @@
"accounting_dimensions_section", "accounting_dimensions_section",
"cost_center", "cost_center",
"dimension_col_break", "dimension_col_break",
"project",
"currency_section", "currency_section",
"account_currency", "account_currency",
"column_break_10", "column_break_10",
@@ -32,7 +33,6 @@
"reference_type", "reference_type",
"reference_name", "reference_name",
"reference_due_date", "reference_due_date",
"project",
"col_break3", "col_break3",
"is_advance", "is_advance",
"user_remark", "user_remark",
@@ -273,7 +273,7 @@
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-04-25 01:47:49.060128", "modified": "2020-06-18 14:06:54.833738",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry Account", "name": "Journal Entry Account",

View File

@@ -319,7 +319,7 @@ class PaymentEntry(AccountsController):
invoice_payment_amount_map.setdefault(key, 0.0) invoice_payment_amount_map.setdefault(key, 0.0)
invoice_payment_amount_map[key] += reference.allocated_amount invoice_payment_amount_map[key] += reference.allocated_amount
if not invoice_paid_amount_map.get(reference.reference_name): if not invoice_paid_amount_map.get(key):
payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name}, payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name},
fields=['paid_amount', 'payment_amount', 'payment_term']) fields=['paid_amount', 'payment_amount', 'payment_term'])
for term in payment_schedule: for term in payment_schedule:
@@ -332,10 +332,12 @@ class PaymentEntry(AccountsController):
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
else: else:
outstanding = invoice_paid_amount_map.get(key)['outstanding'] outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding'))
if amount > outstanding: if amount > outstanding:
frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0])) frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0]))
if amount and outstanding:
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
@@ -451,6 +453,8 @@ class PaymentEntry(AccountsController):
frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction")) frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
def set_remarks(self): def set_remarks(self):
if self.remarks: return
if self.payment_type=="Internal Transfer": if self.payment_type=="Internal Transfer":
remarks = [_("Amount {0} {1} transferred from {2} to {3}") remarks = [_("Amount {0} {1} transferred from {2} to {3}")
.format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)] .format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)]
@@ -1091,6 +1095,10 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount): def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
references = [] references = []
for payment_term in payment_schedule: for payment_term in payment_schedule:
payment_term_outstanding = flt(payment_term.payment_amount - payment_term.paid_amount,
payment_term.precision('payment_amount'))
if payment_term_outstanding:
references.append({ references.append({
'reference_doctype': dt, 'reference_doctype': dt,
'reference_name': dn, 'reference_name': dn,
@@ -1099,8 +1107,7 @@ def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_tota
'total_amount': grand_total, 'total_amount': grand_total,
'outstanding_amount': outstanding_amount, 'outstanding_amount': outstanding_amount,
'payment_term': payment_term.payment_term, 'payment_term': payment_term.payment_term,
'allocated_amount': flt(payment_term.payment_amount - payment_term.paid_amount, 'allocated_amount': payment_term_outstanding
payment_term.precision('payment_amount'))
}) })
return references return references

View File

@@ -349,9 +349,10 @@
"read_only": 1 "read_only": 1
} }
], ],
"in_create": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-05-08 10:23:02.815237", "modified": "2020-05-29 17:38:49.392713",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Payment Request", "name": "Payment Request",

View File

@@ -17,6 +17,8 @@ from six import string_types
apply_on_dict = {"Item Code": "items", apply_on_dict = {"Item Code": "items",
"Item Group": "item_groups", "Brand": "brands"} "Item Group": "item_groups", "Brand": "brands"}
other_fields = ["other_item_code", "other_item_group", "other_brand"]
class PricingRule(Document): class PricingRule(Document):
def validate(self): def validate(self):
self.validate_mandatory() self.validate_mandatory()
@@ -47,6 +49,13 @@ class PricingRule(Document):
if tocheck and not self.get(tocheck): if tocheck and not self.get(tocheck):
throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError) throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
if self.apply_rule_on_other:
o_field = 'other_' + frappe.scrub(self.apply_rule_on_other)
if not self.get(o_field) and o_field in other_fields:
frappe.throw(_("For the 'Apply Rule On Other' condition the field {0} is mandatory")
.format(frappe.bold(self.apply_rule_on_other)))
if self.price_or_product_discount == 'Price' and not self.rate_or_discount: if self.price_or_product_discount == 'Price' and not self.rate_or_discount:
throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError) throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError)
@@ -80,13 +89,27 @@ class PricingRule(Document):
for f in options: for f in options:
if not f: continue if not f: continue
f = frappe.scrub(f) scrubbed_f = frappe.scrub(f)
if f!=fieldname:
self.set(f, None) if logic_field == 'apply_on':
apply_on_f = apply_on_dict.get(f, f)
else:
apply_on_f = scrubbed_f
if scrubbed_f != fieldname:
self.set(apply_on_f, None)
if self.mixed_conditions and self.get("same_item"): if self.mixed_conditions and self.get("same_item"):
self.same_item = 0 self.same_item = 0
apply_rule_on_other = frappe.scrub(self.apply_rule_on_other or "")
cleanup_other_fields = (other_fields if not apply_rule_on_other
else [o_field for o_field in other_fields if o_field != 'other_' + apply_rule_on_other])
for other_field in cleanup_other_fields:
self.set(other_field, None)
def validate_rate_or_discount(self): def validate_rate_or_discount(self):
for field in ["Rate"]: for field in ["Rate"]:
if flt(self.get(frappe.scrub(field))) < 0: if flt(self.get(frappe.scrub(field))) < 0:

View File

@@ -386,6 +386,50 @@ class TestPricingRule(unittest.TestCase):
self.assertEqual(so.items[1].is_free_item, 1) self.assertEqual(so.items[1].is_free_item, 1)
self.assertEqual(so.items[1].item_code, "_Test Item 2") self.assertEqual(so.items[1].item_code, "_Test Item 2")
def test_cumulative_pricing_rule(self):
frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule')
test_record = {
"doctype": "Pricing Rule",
"title": "_Test Cumulative Pricing Rule",
"apply_on": "Item Code",
"currency": "USD",
"items": [{
"item_code": "_Test Item",
}],
"is_cumulative": 1,
"selling": 1,
"applicable_for": "Customer",
"customer": "_Test Customer",
"rate_or_discount": "Discount Percentage",
"rate": 0,
"min_amt": 0,
"max_amt": 10000,
"discount_percentage": 17.5,
"price_or_product_discount": "Price",
"company": "_Test Company",
"valid_from": frappe.utils.nowdate(),
"valid_upto": frappe.utils.nowdate()
}
frappe.get_doc(test_record.copy()).insert()
args = frappe._dict({
"item_code": "_Test Item",
"company": "_Test Company",
"price_list": "_Test Price List",
"currency": "_Test Currency",
"doctype": "Sales Invoice",
"conversion_rate": 1,
"price_list_currency": "_Test Currency",
"plc_conversion_rate": 1,
"order_type": "Sales",
"customer": "_Test Customer",
"name": None,
"transaction_date": frappe.utils.nowdate()
})
details = get_item_details(args)
self.assertTrue(details)
def make_pricing_rule(**args): def make_pricing_rule(**args):
args = frappe._dict(args) args = frappe._dict(args)

View File

@@ -366,8 +366,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]):
sum_qty, sum_amt = [0, 0] sum_qty, sum_amt = [0, 0]
doctype = doc.get('parenttype') or doc.doctype doctype = doc.get('parenttype') or doc.doctype
date_field = ('transaction_date' date_field = 'transaction_date' if frappe.get_meta(doctype).has_field('transaction_date') else 'posting_date'
if doc.get('transaction_date') else 'posting_date')
child_doctype = '{0} Item'.format(doctype) child_doctype = '{0} Item'.format(doctype)
apply_on = frappe.scrub(pr_doc.get('apply_on')) apply_on = frappe.scrub(pr_doc.get('apply_on'))

View File

@@ -238,6 +238,12 @@ class PurchaseInvoice(BuyingController):
not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")): not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")):
if self.update_stock and (not item.from_warehouse): if self.update_stock and (not item.from_warehouse):
if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]:
frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because account {2}
is not linked to warehouse {3} or it is not the default inventory account'''.format(
item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]),
frappe.bold(item.expense_account), frappe.bold(item.warehouse))))
item.expense_account = warehouse_account[item.warehouse]["account"] item.expense_account = warehouse_account[item.warehouse]["account"]
else: else:
# check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not # check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not
@@ -247,10 +253,21 @@ class PurchaseInvoice(BuyingController):
(item.purchase_receipt, stock_not_billed_account)) (item.purchase_receipt, stock_not_billed_account))
if negative_expense_booked_in_pr: if negative_expense_booked_in_pr:
if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because
expense is booked against this account in Purchase Receipt {2}'''.format(
item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.purchase_receipt))))
item.expense_account = stock_not_billed_account item.expense_account = stock_not_billed_account
else: else:
# If no purchase receipt present then book expense in 'Stock Received But Not Billed' # If no purchase receipt present then book expense in 'Stock Received But Not Billed'
# This is done in cases when Purchase Invoice is created before Purchase Receipt # This is done in cases when Purchase Invoice is created before Purchase Receipt
if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
frappe.msgprint(_('''Row {0}: Expense Head changed to {1} as no Purchase
Receipt is created against Item {2}. This is done to handle accounting for cases
when Purchase Receipt is created after Purchase Invoice'''.format(
item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.item_code))))
item.expense_account = stock_not_billed_account item.expense_account = stock_not_billed_account
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category): elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):

View File

@@ -16,7 +16,7 @@ frappe.listview_settings['Purchase Invoice'] = {
} else if(frappe.datetime.get_diff(doc.due_date) < 0) { } else if(frappe.datetime.get_diff(doc.due_date) < 0) {
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"]; return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"];
} else { } else {
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due,>=,Today"]; return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"];
} }
} else if(cint(doc.is_return)) { } else if(cint(doc.is_return)) {
return [__("Return"), "darkgrey", "is_return,=,Yes"]; return [__("Return"), "darkgrey", "is_return,=,Yes"];

View File

@@ -582,14 +582,14 @@ class SalesInvoice(SellingController):
def validate_item_code(self): def validate_item_code(self):
for d in self.get('items'): for d in self.get('items'):
if not d.item_code: if not d.item_code and self.is_opening == "No":
msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True) msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True)
def validate_warehouse(self): def validate_warehouse(self):
super(SalesInvoice, self).validate_warehouse() super(SalesInvoice, self).validate_warehouse()
for d in self.get_item_list(): for d in self.get_item_list():
if not d.warehouse and frappe.get_cached_value("Item", d.item_code, "is_stock_item"): if not d.warehouse and d.item_code and frappe.get_cached_value("Item", d.item_code, "is_stock_item"):
frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code)) frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code))
def validate_delivery_note(self): def validate_delivery_note(self):
@@ -1450,11 +1450,17 @@ def get_inter_company_details(doc, doctype):
parties = frappe.db.get_all("Supplier", fields=["name"], filters={"disabled": 0, "is_internal_supplier": 1, "represents_company": doc.company}) parties = frappe.db.get_all("Supplier", fields=["name"], filters={"disabled": 0, "is_internal_supplier": 1, "represents_company": doc.company})
company = frappe.get_cached_value("Customer", doc.customer, "represents_company") company = frappe.get_cached_value("Customer", doc.customer, "represents_company")
if not parties:
frappe.throw(_('No Supplier found for Inter Company Transactions which represents company {0}').format(frappe.bold(doc.company)))
party = get_internal_party(parties, "Supplier", doc) party = get_internal_party(parties, "Supplier", doc)
else: else:
parties = frappe.db.get_all("Customer", fields=["name"], filters={"disabled": 0, "is_internal_customer": 1, "represents_company": doc.company}) parties = frappe.db.get_all("Customer", fields=["name"], filters={"disabled": 0, "is_internal_customer": 1, "represents_company": doc.company})
company = frappe.get_cached_value("Supplier", doc.supplier, "represents_company") company = frappe.get_cached_value("Supplier", doc.supplier, "represents_company")
if not parties:
frappe.throw(_('No Customer found for Inter Company Transactions which represents company {0}').format(frappe.bold(doc.company)))
party = get_internal_party(parties, "Customer", doc) party = get_internal_party(parties, "Customer", doc)
return { return {
@@ -1519,14 +1525,22 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
def update_details(source_doc, target_doc, source_parent): def update_details(source_doc, target_doc, source_parent):
target_doc.inter_company_invoice_reference = source_doc.name target_doc.inter_company_invoice_reference = source_doc.name
if target_doc.doctype in ["Purchase Invoice", "Purchase Order"]: if target_doc.doctype in ["Purchase Invoice", "Purchase Order"]:
currency = frappe.db.get_value('Supplier', details.get('party'), 'default_currency')
target_doc.company = details.get("company") target_doc.company = details.get("company")
target_doc.supplier = details.get("party") target_doc.supplier = details.get("party")
target_doc.buying_price_list = source_doc.selling_price_list target_doc.buying_price_list = source_doc.selling_price_list
if currency:
target_doc.currency = currency
else: else:
currency = frappe.db.get_value('Customer', details.get('party'), 'default_currency')
target_doc.company = details.get("company") target_doc.company = details.get("company")
target_doc.customer = details.get("party") target_doc.customer = details.get("party")
target_doc.selling_price_list = source_doc.buying_price_list target_doc.selling_price_list = source_doc.buying_price_list
if currency:
target_doc.currency = currency
doclist = get_mapped_doc(doctype, source_name, { doclist = get_mapped_doc(doctype, source_name, {
doctype: { doctype: {
"doctype": target_doctype, "doctype": target_doctype,

View File

@@ -1745,53 +1745,6 @@ class TestSalesInvoice(unittest.TestCase):
check_gl_entries(self, si.name, expected_gle, "2019-01-30") check_gl_entries(self, si.name, expected_gle, "2019-01-30")
def test_deferred_error_email(self):
deferred_account = create_account(account_name="Deferred Revenue",
parent_account="Current Liabilities - _TC", company="_Test Company")
item = create_item("_Test Item for Deferred Accounting")
item.enable_deferred_revenue = 1
item.deferred_revenue_account = deferred_account
item.no_of_months = 12
item.save()
si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_submit=True)
si.items[0].enable_deferred_revenue = 1
si.items[0].service_start_date = "2019-01-10"
si.items[0].service_end_date = "2019-03-15"
si.items[0].deferred_revenue_account = deferred_account
si.save()
si.submit()
from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income
acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
acc_settings.acc_frozen_upto = '2019-01-31'
acc_settings.save()
pda = frappe.get_doc(dict(
doctype='Process Deferred Accounting',
posting_date=nowdate(),
start_date="2019-01-01",
end_date="2019-03-31",
type="Income",
company="_Test Company"
))
pda.insert()
pda.submit()
email = frappe.db.sql(""" select name from `tabEmail Queue`
where message like %(txt)s """, {
'txt': "%%%s%%" % "Error while processing deferred accounting for {0}".format(pda.name)
})
self.assertTrue(email)
acc_settings.load_from_db()
acc_settings.acc_frozen_upto = None
acc_settings.save()
def test_inter_company_transaction(self): def test_inter_company_transaction(self):
if not frappe.db.exists("Customer", "_Test Internal Customer"): if not frappe.db.exists("Customer", "_Test Internal Customer"):

View File

@@ -45,6 +45,8 @@ class ShippingRule(Document):
shipping_amount = 0.0 shipping_amount = 0.0
by_value = False by_value = False
if doc.get_shipping_address():
# validate country only if there is address
self.validate_countries(doc) self.validate_countries(doc)
if self.calculate_based_on == 'Net Total': if self.calculate_based_on == 'Net Total':

View File

@@ -61,7 +61,7 @@ def make_sales_invoice():
debit_to = 'Debtors - _TC2', debit_to = 'Debtors - _TC2',
income_account = 'Sales - _TC2', income_account = 'Sales - _TC2',
expense_account = 'Cost of Goods Sold - _TC2', expense_account = 'Cost of Goods Sold - _TC2',
cost_center = '_Test Company 2 - _TC2') cost_center = 'Main - _TC2')

View File

@@ -169,6 +169,8 @@ class ReceivablePayableReport(object):
def append_subtotal_row(self, party): def append_subtotal_row(self, party):
sub_total_row = self.total_row_map.get(party) sub_total_row = self.total_row_map.get(party)
if sub_total_row:
self.data.append(sub_total_row) self.data.append(sub_total_row)
self.data.append({}) self.data.append({})
self.update_sub_total_row(sub_total_row, 'Total') self.update_sub_total_row(sub_total_row, 'Total')
@@ -232,6 +234,7 @@ class ReceivablePayableReport(object):
if self.filters.get('group_by_party'): if self.filters.get('group_by_party'):
self.append_subtotal_row(self.previous_party) self.append_subtotal_row(self.previous_party)
if self.data:
self.data.append(self.total_row_map.get('Total')) self.data.append(self.total_row_map.get('Total'))
def append_row(self, row): def append_row(self, row):

View File

@@ -63,7 +63,7 @@ def make_sales_invoice():
debit_to = 'Debtors - _TC2', debit_to = 'Debtors - _TC2',
income_account = 'Sales - _TC2', income_account = 'Sales - _TC2',
expense_account = 'Cost of Goods Sold - _TC2', expense_account = 'Cost of Goods Sold - _TC2',
cost_center = '_Test Company 2 - _TC2', cost_center = 'Main - _TC2',
do_not_save=1) do_not_save=1)
si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30)) si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30))
@@ -90,7 +90,7 @@ def make_credit_note(docname):
debit_to = 'Debtors - _TC2', debit_to = 'Debtors - _TC2',
income_account = 'Sales - _TC2', income_account = 'Sales - _TC2',
expense_account = 'Cost of Goods Sold - _TC2', expense_account = 'Cost of Goods Sold - _TC2',
cost_center = '_Test Company 2 - _TC2', cost_center = 'Main - _TC2',
is_return = 1, is_return = 1,
return_against = docname) return_against = docname)

View File

@@ -93,7 +93,7 @@ def get_assets(filters):
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period, sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
from (SELECT a.asset_category, from (SELECT a.asset_category,
ifnull(sum(case when ds.schedule_date < %(from_date)s then ifnull(sum(case when ds.schedule_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
ds.depreciation_amount ds.depreciation_amount
else else
0 0
@@ -115,9 +115,7 @@ def get_assets(filters):
group by a.asset_category group by a.asset_category
union union
SELECT a.asset_category, SELECT a.asset_category,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then
and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s)
then
0 0
else else
a.opening_accumulated_depreciation a.opening_accumulated_depreciation

View File

@@ -29,6 +29,26 @@ def execute(filters=None):
for dimension in dimensions: for dimension in dimensions:
dimension_items = cam_map.get(dimension) dimension_items = cam_map.get(dimension)
if dimension_items: if dimension_items:
data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, 0)
else:
DCC_allocation = frappe.db.sql('''SELECT parent, sum(percentage_allocation) as percentage_allocation
FROM `tabDistributed Cost Center`
WHERE cost_center IN %(dimension)s
AND parent NOT IN %(dimension)s
GROUP BY parent''',{'dimension':[dimension]})
if DCC_allocation:
filters['budget_against_filter'] = [DCC_allocation[0][0]]
cam_map = get_dimension_account_month_map(filters)
dimension_items = cam_map.get(DCC_allocation[0][0])
if dimension_items:
data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation[0][1])
chart = get_chart_data(filters, columns, data)
return columns, data, None, chart
def get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation):
for account, monthwise_data in iteritems(dimension_items): for account, monthwise_data in iteritems(dimension_items):
row = [dimension, account] row = [dimension, account]
totals = [0, 0, 0] totals = [0, 0, 0]
@@ -46,7 +66,11 @@ def execute(filters=None):
period_data[0] += last_total period_data[0] += last_total
if filters.get("show_cumulative"): if DCC_allocation:
period_data[0] = period_data[0]*(DCC_allocation/100)
period_data[1] = period_data[1]*(DCC_allocation/100)
if(filters.get("show_cumulative")):
last_total = period_data[0] - period_data[1] last_total = period_data[0] - period_data[1]
period_data[2] = period_data[0] - period_data[1] period_data[2] = period_data[0] - period_data[1]
@@ -56,9 +80,8 @@ def execute(filters=None):
row += totals row += totals
data.append(row) data.append(row)
chart = get_chart_data(filters, columns, data) return data
return columns, data, None, chart
def get_columns(filters): def get_columns(filters):
columns = [ columns = [

View File

@@ -33,7 +33,6 @@ frappe.query_reports["Consolidated Financial Statement"] = {
"fieldname":"period_start_date", "fieldname":"period_start_date",
"label": __("Start Date"), "label": __("Start Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": frappe.datetime.nowdate(),
"hidden": 1, "hidden": 1,
"reqd": 1 "reqd": 1
}, },
@@ -41,7 +40,6 @@ frappe.query_reports["Consolidated Financial Statement"] = {
"fieldname":"period_end_date", "fieldname":"period_end_date",
"label": __("End Date"), "label": __("End Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12),
"hidden": 1, "hidden": 1,
"reqd": 1 "reqd": 1
}, },
@@ -106,5 +104,16 @@ frappe.query_reports["Consolidated Financial Statement"] = {
value = $value.wrap("<p></p>").parent().html(); value = $value.wrap("<p></p>").parent().html();
} }
return value; return value;
},
onload: function() {
let fiscal_year = frappe.defaults.get_user_default("fiscal_year")
frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
frappe.query_report.set_filter_value({
period_start_date: fy.year_start_date,
period_end_date: fy.year_end_date
});
});
} }
} }

View File

@@ -56,8 +56,7 @@ def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_
to_date = add_months(start_date, months_to_add) to_date = add_months(start_date, months_to_add)
start_date = to_date start_date = to_date
if to_date == get_first_day(to_date): # Subtract one day from to_date, as it may be first day in next fiscal year or month
# if to_date is the first day, get the last day of previous month
to_date = add_days(to_date, -1) to_date = add_days(to_date, -1)
if to_date <= year_end_date: if to_date <= year_end_date:
@@ -387,11 +386,43 @@ def set_gl_entries_by_account(
key: value key: value
}) })
distributed_cost_center_query = ""
if filters and filters.get('cost_center'):
distributed_cost_center_query = """
UNION ALL
SELECT posting_date,
account,
debit*(DCC_allocation.percentage_allocation/100) as debit,
credit*(DCC_allocation.percentage_allocation/100) as credit,
is_opening,
fiscal_year,
debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency,
credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency,
account_currency
FROM `tabGL Entry`,
(
SELECT parent, sum(percentage_allocation) as percentage_allocation
FROM `tabDistributed Cost Center`
WHERE cost_center IN %(cost_center)s
AND parent NOT IN %(cost_center)s
AND is_cancelled = 0
GROUP BY parent
) as DCC_allocation
WHERE company=%(company)s
{additional_conditions}
AND posting_date <= %(to_date)s
AND cost_center = DCC_allocation.parent
""".format(additional_conditions=additional_conditions.replace("and cost_center in %(cost_center)s ", ''))
gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry` gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry`
where company=%(company)s where company=%(company)s
{additional_conditions} {additional_conditions}
and posting_date <= %(to_date)s and posting_date <= %(to_date)s
order by account, posting_date""".format(additional_conditions=additional_conditions), gl_filters, as_dict=True) #nosec and is_cancelled = 0
{distributed_cost_center_query}
order by account, posting_date""".format(
additional_conditions=additional_conditions,
distributed_cost_center_query=distributed_cost_center_query), gl_filters, as_dict=True) #nosec
if filters and filters.get('presentation_currency'): if filters and filters.get('presentation_currency'):
convert_to_presentation_currency(gl_entries, get_currency(filters)) convert_to_presentation_currency(gl_entries, get_currency(filters))

View File

@@ -128,18 +128,53 @@ def get_gl_entries(filters):
filters['company_fb'] = frappe.db.get_value("Company", filters['company_fb'] = frappe.db.get_value("Company",
filters.get("company"), 'default_finance_book') filters.get("company"), 'default_finance_book')
distributed_cost_center_query = ""
if filters and filters.get('cost_center'):
select_fields_with_percentage = """, debit*(DCC_allocation.percentage_allocation/100) as debit, credit*(DCC_allocation.percentage_allocation/100) as credit, debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency,
credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency """
distributed_cost_center_query = """
UNION ALL
SELECT name as gl_entry,
posting_date,
account,
party_type,
party,
voucher_type,
voucher_no,
cost_center, project,
against_voucher_type,
against_voucher,
account_currency,
remarks, against,
is_opening, `tabGL Entry`.creation {select_fields_with_percentage}
FROM `tabGL Entry`,
(
SELECT parent, sum(percentage_allocation) as percentage_allocation
FROM `tabDistributed Cost Center`
WHERE cost_center IN %(cost_center)s
AND parent NOT IN %(cost_center)s
GROUP BY parent
) as DCC_allocation
WHERE company=%(company)s
{conditions}
AND posting_date <= %(to_date)s
AND cost_center = DCC_allocation.parent
""".format(select_fields_with_percentage=select_fields_with_percentage, conditions=get_conditions(filters).replace("and cost_center in %(cost_center)s ", ''))
gl_entries = frappe.db.sql( gl_entries = frappe.db.sql(
""" """
select select
name as gl_entry, posting_date, account, party_type, party, name as gl_entry, posting_date, account, party_type, party,
voucher_type, voucher_no, cost_center, project, voucher_type, voucher_no, cost_center, project,
against_voucher_type, against_voucher, account_currency, against_voucher_type, against_voucher, account_currency,
remarks, against, is_opening {select_fields} remarks, against, is_opening, creation {select_fields}
from `tabGL Entry` from `tabGL Entry`
where company=%(company)s {conditions} where company=%(company)s {conditions}
{distributed_cost_center_query}
{order_by_statement} {order_by_statement}
""".format( """.format(
select_fields=select_fields, conditions=get_conditions(filters), select_fields=select_fields, conditions=get_conditions(filters), distributed_cost_center_query=distributed_cost_center_query,
order_by_statement=order_by_statement order_by_statement=order_by_statement
), ),
filters, as_dict=1) filters, as_dict=1)

View File

@@ -265,13 +265,6 @@ def get_columns(additional_table_columns, filters):
'fieldtype': 'Currency', 'fieldtype': 'Currency',
'options': 'currency', 'options': 'currency',
'width': 100 'width': 100
},
{
'fieldname': 'currency',
'label': _('Currency'),
'fieldtype': 'Currency',
'width': 80,
'hidden': 1
} }
] ]

View File

@@ -223,7 +223,7 @@ def get_columns(additional_table_columns, filters):
} }
] ]
if filters.get('group_by') != 'Terriotory': if filters.get('group_by') != 'Territory':
columns.extend([ columns.extend([
{ {
'label': _("Territory"), 'label': _("Territory"),
@@ -304,13 +304,6 @@ def get_columns(additional_table_columns, filters):
'fieldtype': 'Currency', 'fieldtype': 'Currency',
'options': 'currency', 'options': 'currency',
'width': 100 'width': 100
},
{
'fieldname': 'currency',
'label': _('Currency'),
'fieldtype': 'Currency',
'width': 80,
'hidden': 1
} }
] ]
@@ -536,6 +529,13 @@ def get_tax_accounts(item_list, columns, company_currency,
'fieldtype': 'Currency', 'fieldtype': 'Currency',
'options': 'currency', 'options': 'currency',
'width': 100 'width': 100
},
{
'fieldname': 'currency',
'label': _('Currency'),
'fieldtype': 'Currency',
'width': 80,
'hidden': 1
} }
] ]

View File

@@ -1,8 +0,0 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.query_reports["Ordered Items To Be Billed"] = {
"filters": [
]
}

View File

@@ -1,27 +0,0 @@
{
"add_total_row": 1,
"apply_user_permissions": 1,
"creation": "2013-02-21 14:26:44",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"is_standard": "Yes",
"modified": "2017-11-06 13:04:51.559061",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Ordered Items To Be Billed",
"owner": "Administrator",
"query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`customer_name` as \"Customer Name:150\",\n`tabSales Order`.`status` as \"Status\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project` as \"Project\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.base_amount as \"Amount:Currency:110\",\n (`tabSales Order Item`.billed_amt * ifnull(`tabSales Order`.conversion_rate, 1)) as \"Billed Amount:Currency:110\",\n (`tabSales Order Item`.base_amount - (`tabSales Order Item`.billed_amt * ifnull(`tabSales Order`.conversion_rate, 1))) as \"Pending Amount:Currency:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order`.`company` as \"Company:Link/Company:\"\nfrom\n `tabSales Order`, `tabSales Order Item`\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status != \"Closed\"\n and `tabSales Order Item`.amount > 0\n and `tabSales Order Item`.billed_amt < `tabSales Order Item`.amount\norder by `tabSales Order`.transaction_date asc",
"ref_doctype": "Sales Invoice",
"report_name": "Ordered Items To Be Billed",
"report_type": "Script Report",
"roles": [
{
"role": "Accounts Manager"
},
{
"role": "Accounts User"
}
]
}

View File

@@ -1,26 +0,0 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_data
def execute(filters=None):
columns = get_column()
args = get_args()
data = get_ordered_to_be_billed_data(args)
return columns, data
def get_column():
return [
_("Sales Order") + ":Link/Sales Order:120", _("Status") + "::120", _("Date") + ":Date:100",
_("Suplier") + ":Link/Customer:120", _("Customer Name") + "::120",
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Pending Amount") + ":Currency:100",
_("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120",
]
def get_args():
return {'doctype': 'Sales Order', 'party': 'customer',
'date': 'transaction_date', 'order': 'transaction_date', 'order_by': 'asc'}

View File

@@ -105,6 +105,7 @@ def accumulate_values_into_parents(accounts, accounts_by_name):
def prepare_data(accounts, filters, total_row, parent_children_map, based_on): def prepare_data(accounts, filters, total_row, parent_children_map, based_on):
data = [] data = []
new_accounts = accounts
company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency") company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency")
for d in accounts: for d in accounts:
@@ -118,6 +119,19 @@ def prepare_data(accounts, filters, total_row, parent_children_map, based_on):
"currency": company_currency, "currency": company_currency,
"based_on": based_on "based_on": based_on
} }
if based_on == 'cost_center':
cost_center_doc = frappe.get_doc("Cost Center",d.name)
if not cost_center_doc.enable_distributed_cost_center:
DCC_allocation = frappe.db.sql("""SELECT parent, sum(percentage_allocation) as percentage_allocation
FROM `tabDistributed Cost Center`
WHERE cost_center IN %(cost_center)s
AND parent NOT IN %(cost_center)s
GROUP BY parent""",{'cost_center': [d.name]})
if DCC_allocation:
for account in new_accounts:
if account['name'] == DCC_allocation[0][0]:
for value in value_fields:
d[value] += account[value]*(DCC_allocation[0][1]/100)
for key in value_fields: for key in value_fields:
row[key] = flt(d.get(key, 0.0), 3) row[key] = flt(d.get(key, 0.0), 3)

View File

@@ -111,7 +111,7 @@ def get_gle_map(filters):
# {"purchase_invoice": list of dict of all gle created for this invoice} # {"purchase_invoice": list of dict of all gle created for this invoice}
gle_map = {} gle_map = {}
gle = frappe.db.get_all('GL Entry',\ gle = frappe.db.get_all('GL Entry',\
{"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]]}, {"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]], 'is_cancelled': 0},
["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date"]) ["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date"])
for d in gle: for d in gle:

View File

@@ -57,6 +57,9 @@ def get_fiscal_years(transaction_date=None, fiscal_year=None, label="Date", verb
frappe.cache().hset("fiscal_years", company, fiscal_years) frappe.cache().hset("fiscal_years", company, fiscal_years)
if not transaction_date and not fiscal_year:
return fiscal_years
if transaction_date: if transaction_date:
transaction_date = getdate(transaction_date) transaction_date = getdate(transaction_date)
@@ -79,6 +82,23 @@ def get_fiscal_years(transaction_date=None, fiscal_year=None, label="Date", verb
if verbose==1: frappe.msgprint(error_msg) if verbose==1: frappe.msgprint(error_msg)
raise FiscalYearError(error_msg) raise FiscalYearError(error_msg)
@frappe.whitelist()
def get_fiscal_year_filter_field(company=None):
field = {
"fieldtype": "Select",
"options": [],
"operator": "Between",
"query_value": True
}
fiscal_years = get_fiscal_years(company=company)
for fiscal_year in fiscal_years:
field["options"].append({
"label": fiscal_year.name,
"value": fiscal_year.name,
"query_value": [fiscal_year.year_start_date.strftime("%Y-%m-%d"), fiscal_year.year_end_date.strftime("%Y-%m-%d")]
})
return field
def validate_fiscal_year(date, fiscal_year, company, label="Date", doc=None): def validate_fiscal_year(date, fiscal_year, company, label="Date", doc=None):
years = [f[0] for f in get_fiscal_years(date, label=_(label), company=company)] years = [f[0] for f in get_fiscal_years(date, label=_(label), company=company)]
if fiscal_year not in years: if fiscal_year not in years:
@@ -817,7 +837,7 @@ def create_payment_gateway_account(gateway):
pass pass
@frappe.whitelist() @frappe.whitelist()
def update_cost_center(docname, cost_center_name, cost_center_number, company): def update_cost_center(docname, cost_center_name, cost_center_number, company, merge):
''' '''
Renames the document by adding the number as a prefix to the current name and updates Renames the document by adding the number as a prefix to the current name and updates
all transaction where it was present. all transaction where it was present.
@@ -833,7 +853,7 @@ def update_cost_center(docname, cost_center_name, cost_center_number, company):
new_name = get_autoname_with_number(cost_center_number, cost_center_name, docname, company) new_name = get_autoname_with_number(cost_center_number, cost_center_name, docname, company)
if docname != new_name: if docname != new_name:
frappe.rename_doc("Cost Center", docname, new_name, force=1) frappe.rename_doc("Cost Center", docname, new_name, force=1, merge=merge)
return new_name return new_name
def validate_field_number(doctype_name, docname, number_value, company, field_name): def validate_field_number(doctype_name, docname, number_value, company, field_name):

View File

@@ -5,14 +5,23 @@ import frappe
import json import json
from frappe.utils import nowdate, add_months, get_date_str from frappe.utils import nowdate, add_months, get_date_str
from frappe import _ from frappe import _
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.dashboard_fixtures import _get_fiscal_year
from erpnext.buying.dashboard_fixtures import get_company_for_dashboards
def get_data(): def get_data():
fiscal_year = _get_fiscal_year(nowdate())
if not fiscal_year:
return frappe._dict()
year_start_date = get_date_str(fiscal_year.get('year_start_date'))
year_end_date = get_date_str(fiscal_year.get('year_end_date'))
return frappe._dict({ return frappe._dict({
"dashboards": get_dashboards(), "dashboards": get_dashboards(),
"charts": get_charts(), "charts": get_charts(fiscal_year, year_start_date, year_end_date),
"number_cards": get_number_cards(), "number_cards": get_number_cards(fiscal_year, year_start_date, year_end_date),
}) })
def get_dashboards(): def get_dashboards():
@@ -31,12 +40,7 @@ def get_dashboards():
] ]
}] }]
fiscal_year = get_fiscal_year(date=nowdate()) def get_charts(fiscal_year, year_start_date, year_end_date):
year_start_date = get_date_str(fiscal_year[1])
year_end_date = get_date_str(fiscal_year[2])
def get_charts():
company = get_company_for_dashboards() company = get_company_for_dashboards()
return [ return [
{ {
@@ -55,8 +59,8 @@ def get_charts():
"company": company, "company": company,
"status": "In Location", "status": "In Location",
"filter_based_on": "Fiscal Year", "filter_based_on": "Fiscal Year",
"from_fiscal_year": fiscal_year[0], "from_fiscal_year": fiscal_year.get('name'),
"to_fiscal_year": fiscal_year[0], "to_fiscal_year": fiscal_year.get('name'),
"period_start_date": year_start_date, "period_start_date": year_start_date,
"period_end_date": year_end_date, "period_end_date": year_end_date,
"date_based_on": "Purchase Date", "date_based_on": "Purchase Date",
@@ -134,7 +138,7 @@ def get_charts():
} }
] ]
def get_number_cards(): def get_number_cards(fiscal_year, year_start_date, year_end_date):
return [ return [
{ {
"name": "Total Assets", "name": "Total Assets",
@@ -173,13 +177,3 @@ def get_number_cards():
"doctype": "Number Card" "doctype": "Number Card"
} }
] ]
def get_company_for_dashboards():
company = frappe.defaults.get_defaults().company
if company:
return company
else:
company_list = frappe.get_list("Company")
if company_list:
return company_list[0].name
return None

View File

@@ -1,559 +1,140 @@
{ {
"allow_copy": 0, "actions": [],
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:asset_name", "autoname": "field:asset_name",
"beta": 0,
"creation": "2017-10-19 16:50:22.879545", "creation": "2017-10-19 16:50:22.879545",
"custom": 0,
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [
"asset_name",
"asset_category",
"company",
"column_break_3",
"item_code",
"item_name",
"section_break_6",
"maintenance_team",
"column_break_9",
"maintenance_manager",
"maintenance_manager_name",
"section_break_8",
"asset_maintenance_tasks"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "asset_name", "fieldname": "asset_name",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Asset Name", "label": "Asset Name",
"length": 0,
"no_copy": 0,
"options": "Asset", "options": "Asset",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0, "unique": 1
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "asset_name.asset_category", "fetch_from": "asset_name.asset_category",
"fieldname": "asset_category", "fieldname": "asset_category",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"hidden": 0, "label": "Asset Category"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Asset Category",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "asset_name.item_code", "fetch_from": "asset_name.item_code",
"fieldname": "item_code", "fieldname": "item_code",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"hidden": 0, "label": "Item Code"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Item Code",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "asset_name.item_name", "fetch_from": "asset_name.item_name",
"fieldname": "item_name", "fieldname": "item_name",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"hidden": 0, "label": "Item Name"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Item Name",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3", "fieldname": "column_break_3",
"fieldtype": "Column Break", "fieldtype": "Column Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company", "fieldname": "company",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Company", "label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company", "options": "Company",
"permlevel": 0, "reqd": 1
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "select_serial_no",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Select Serial No",
"length": 0,
"no_copy": 0,
"options": "Serial No",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "serial_no",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Serial No",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_6", "fieldname": "section_break_6",
"fieldtype": "Section Break", "fieldtype": "Section Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "maintenance_team", "fieldname": "maintenance_team",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Maintenance Team", "label": "Maintenance Team",
"length": 0,
"no_copy": 0,
"options": "Asset Maintenance Team", "options": "Asset Maintenance Team",
"permlevel": 0, "reqd": 1
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_9", "fieldname": "column_break_9",
"fieldtype": "Column Break", "fieldtype": "Column Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "maintenance_team.maintenance_manager", "fetch_from": "maintenance_team.maintenance_manager",
"fieldname": "maintenance_manager", "fieldname": "maintenance_manager",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 1, "hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Maintenance Manager", "label": "Maintenance Manager",
"length": 0, "read_only": 1
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "maintenance_team.maintenance_manager_name", "fetch_from": "maintenance_team.maintenance_manager_name",
"fieldname": "maintenance_manager_name", "fieldname": "maintenance_manager_name",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"hidden": 0, "label": "Maintenance Manager Name"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Maintenance Manager Name",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_8", "fieldname": "section_break_8",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "label": "Tasks"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Tasks",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "asset_maintenance_tasks", "fieldname": "asset_maintenance_tasks",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Maintenance Tasks", "label": "Maintenance Tasks",
"length": 0,
"no_copy": 0,
"options": "Asset Maintenance Task", "options": "Asset Maintenance Task",
"permlevel": 0, "reqd": 1
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
} }
], ],
"has_web_view": 0, "links": [],
"hide_heading": 0, "modified": "2020-05-28 20:28:32.993823",
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-05-22 17:20:54.711885",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Asset Maintenance", "name": "Asset Maintenance",
"name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
"amend": 0,
"cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 1, "export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Quality Manager", "role": "Quality Manager",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0,
"write": 1 "write": 1
}, },
{ {
"amend": 0,
"cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 1, "export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Manufacturing User", "role": "Manufacturing User",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0,
"write": 1 "write": 1
} }
], ],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"track_changes": 1, "track_changes": 1
"track_seen": 0
} }

View File

@@ -38,10 +38,10 @@ class AssetMaintenance(Document):
@frappe.whitelist() @frappe.whitelist()
def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, next_due_date): def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, next_due_date):
team_member = frappe.get_doc('User', assign_to_member).email team_member = frappe.db.get_value('User', assign_to_member, "email")
args = { args = {
'doctype' : 'Asset Maintenance', 'doctype' : 'Asset Maintenance',
'assign_to' : team_member, 'assign_to' : [team_member],
'name' : asset_maintenance_name, 'name' : asset_maintenance_name,
'description' : maintenance_task, 'description' : maintenance_task,
'date' : next_due_date 'date' : next_due_date
@@ -77,7 +77,7 @@ def calculate_next_due_date(periodicity, start_date = None, end_date = None, las
def update_maintenance_log(asset_maintenance, item_code, item_name, task): def update_maintenance_log(asset_maintenance, item_code, item_name, task):
asset_maintenance_log = frappe.get_value("Asset Maintenance Log", {"asset_maintenance": asset_maintenance, asset_maintenance_log = frappe.get_value("Asset Maintenance Log", {"asset_maintenance": asset_maintenance,
"task": task.maintenance_task, "maintenance_status": ('in',['Planned','Overdue'])}) "task": task.name, "maintenance_status": ('in',['Planned','Overdue'])})
if not asset_maintenance_log: if not asset_maintenance_log:
asset_maintenance_log = frappe.get_doc({ asset_maintenance_log = frappe.get_doc({
@@ -86,7 +86,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task):
"asset_name": asset_maintenance, "asset_name": asset_maintenance,
"item_code": item_code, "item_code": item_code,
"item_name": item_name, "item_name": item_name,
"task": task.maintenance_task, "task": task.name,
"has_certificate": task.certificate_required, "has_certificate": task.certificate_required,
"description": task.description, "description": task.description,
"assign_to_name": task.assign_to_name, "assign_to_name": task.assign_to_name,

View File

@@ -1,789 +1,190 @@
{ {
"allow_copy": 0, "actions": [],
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "naming_series:", "autoname": "naming_series:",
"beta": 0,
"creation": "2017-10-23 16:58:44.424309", "creation": "2017-10-23 16:58:44.424309",
"custom": 0,
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Document", "document_type": "Document",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [
"asset_maintenance",
"naming_series",
"asset_name",
"column_break_2",
"item_code",
"item_name",
"section_break_5",
"task",
"task_name",
"maintenance_type",
"periodicity",
"assign_to_name",
"column_break_6",
"due_date",
"completion_date",
"maintenance_status",
"section_break_12",
"has_certificate",
"certificate_attachement",
"section_break_6",
"description",
"column_break_9",
"actions_performed",
"amended_from"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "asset_maintenance", "fieldname": "asset_maintenance",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Asset Maintenance", "label": "Asset Maintenance",
"length": 0, "options": "Asset Maintenance"
"no_copy": 0,
"options": "Asset Maintenance",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "naming_series", "fieldname": "naming_series",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Series", "label": "Series",
"length": 0,
"no_copy": 0,
"options": "ACC-AML-.YYYY.-", "options": "ACC-AML-.YYYY.-",
"permlevel": 0, "reqd": 1
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "asset_maintenance.asset_name", "fetch_from": "asset_maintenance.asset_name",
"fieldname": "asset_name", "fieldname": "asset_name",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"hidden": 0, "label": "Asset Name"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Asset Name",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2", "fieldname": "column_break_2",
"fieldtype": "Column Break", "fieldtype": "Column Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "asset_maintenance.item_code", "fetch_from": "asset_maintenance.item_code",
"fieldname": "item_code", "fieldname": "item_code",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"hidden": 0, "label": "Item Code"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Item Code",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "asset_maintenance.item_name", "fetch_from": "asset_maintenance.item_name",
"fieldname": "item_name", "fieldname": "item_name",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"hidden": 0, "label": "Item Name"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Item Name",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_5", "fieldname": "section_break_5",
"fieldtype": "Section Break", "fieldtype": "Section Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "task", "fieldname": "task",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Task", "label": "Task",
"length": 0, "options": "Asset Maintenance Task"
"no_copy": 0,
"options": "Asset Maintenance Task",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "task.maintenance_type", "fetch_from": "task.maintenance_type",
"fieldname": "maintenance_type", "fieldname": "maintenance_type",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"hidden": 0, "label": "Maintenance Type"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Maintenance Type",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "task.periodicity", "fetch_from": "task.periodicity",
"fieldname": "periodicity", "fieldname": "periodicity",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Periodicity", "label": "Periodicity",
"length": 0, "read_only": 1
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "task.assign_to_name", "fetch_from": "task.assign_to_name",
"fieldname": "assign_to_name", "fieldname": "assign_to_name",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"hidden": 0, "label": "Assign To"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Assign To",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_6", "fieldname": "column_break_6",
"fieldtype": "Column Break", "fieldtype": "Column Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "task.next_due_date", "fetch_from": "task.next_due_date",
"fieldname": "due_date", "fieldname": "due_date",
"fieldtype": "Date", "fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Due Date", "label": "Due Date",
"length": 0, "read_only": 1
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "completion_date", "fieldname": "completion_date",
"fieldtype": "Date", "fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0, "label": "Completion Date"
"label": "Completion Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "maintenance_status", "fieldname": "maintenance_status",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1, "in_standard_filter": 1,
"label": "Maintenance Status", "label": "Maintenance Status",
"length": 0,
"no_copy": 0,
"options": "Planned\nCompleted\nCancelled\nOverdue", "options": "Planned\nCompleted\nCancelled\nOverdue",
"permlevel": 0, "reqd": 1
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_12", "fieldname": "section_break_12",
"fieldtype": "Section Break", "fieldtype": "Section Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0, "default": "0",
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "task.certificate_required", "fetch_from": "task.certificate_required",
"fieldname": "has_certificate", "fieldname": "has_certificate",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "label": "Has Certificate "
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Has Certificate ",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.has_certificate", "depends_on": "eval:doc.has_certificate",
"fieldname": "certificate_attachement", "fieldname": "certificate_attachement",
"fieldtype": "Attach", "fieldtype": "Attach",
"hidden": 0, "label": "Certificate"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Certificate",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_6", "fieldname": "section_break_6",
"fieldtype": "Column Break", "fieldtype": "Column Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "task.description", "fetch_from": "task.description",
"fieldname": "description", "fieldname": "description",
"fieldtype": "Read Only", "fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Description", "label": "Description",
"length": 0, "read_only": 1
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_9", "fieldname": "column_break_9",
"fieldtype": "Section Break", "fieldtype": "Section Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 1, "allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "actions_performed", "fieldname": "actions_performed",
"fieldtype": "Text Editor", "fieldtype": "Text Editor",
"hidden": 0, "label": "Actions performed"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Actions performed",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from", "fieldname": "amended_from",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From", "label": "Amended From",
"length": 0,
"no_copy": 1, "no_copy": 1,
"options": "Asset Maintenance Log", "options": "Asset Maintenance Log",
"permlevel": 0,
"precision": "",
"print_hide": 1, "print_hide": 1,
"print_hide_if_no_value": 0, "read_only": 1
"read_only": 1, },
"remember_last_selected_value": 0, {
"report_hide": 0, "fetch_from": "task.maintenance_task",
"reqd": 0, "fieldname": "task_name",
"search_index": 0, "fieldtype": "Data",
"set_only_once": 0, "in_preview": 1,
"translatable": 0, "label": "Task Name",
"unique": 0 "read_only": 1
} }
], ],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "links": [],
"istable": 0, "modified": "2020-05-28 20:51:48.238397",
"max_attachments": 0,
"modified": "2018-08-21 14:44:51.457835",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Asset Maintenance Log", "name": "Asset Maintenance Log",
"name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
@@ -793,27 +194,17 @@
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 1, "export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Manufacturing User", "role": "Manufacturing User",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 1, "submit": 1,
"write": 1 "write": 1
} }
], ],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"title_field": "",
"track_changes": 1, "track_changes": 1,
"track_seen": 1, "track_seen": 1
"track_views": 0
} }

View File

@@ -7,5 +7,4 @@ import frappe
from frappe.model.document import Document from frappe.model.document import Document
class AssetMaintenanceTask(Document): class AssetMaintenanceTask(Document):
def autoname(self): pass
self.name = self.maintenance_task

View File

@@ -5,13 +5,24 @@ import frappe
import json import json
from frappe import _ from frappe import _
from frappe.utils import nowdate from frappe.utils import nowdate
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.dashboard_fixtures import _get_fiscal_year
def get_data(): def get_data():
fiscal_year = _get_fiscal_year(nowdate())
if not fiscal_year:
return frappe._dict()
company = frappe.get_doc("Company", get_company_for_dashboards())
fiscal_year_name = fiscal_year.get("name")
start_date = str(fiscal_year.get("year_start_date"))
end_date = str(fiscal_year.get("year_end_date"))
return frappe._dict({ return frappe._dict({
"dashboards": get_dashboards(), "dashboards": get_dashboards(),
"charts": get_charts(), "charts": get_charts(company, fiscal_year_name, start_date, end_date),
"number_cards": get_number_cards(), "number_cards": get_number_cards(company, fiscal_year_name, start_date, end_date),
}) })
def get_company_for_dashboards(): def get_company_for_dashboards():
@@ -24,12 +35,6 @@ def get_company_for_dashboards():
return company_list[0].name return company_list[0].name
return None return None
company = frappe.get_doc("Company", get_company_for_dashboards())
fiscal_year = get_fiscal_year(nowdate(), as_dict=1)
fiscal_year_name = fiscal_year.get("name")
start_date = str(fiscal_year.get("year_start_date"))
end_date = str(fiscal_year.get("year_end_date"))
def get_dashboards(): def get_dashboards():
return [{ return [{
"name": "Buying", "name": "Buying",
@@ -48,7 +53,7 @@ def get_dashboards():
] ]
}] }]
def get_charts(): def get_charts(company, fiscal_year_name, start_date, end_date):
return [ return [
{ {
"name": "Purchase Order Analysis", "name": "Purchase Order Analysis",
@@ -139,7 +144,7 @@ def get_charts():
} }
] ]
def get_number_cards(): def get_number_cards(company, fiscal_year_name, start_date, end_date):
return [ return [
{ {
"name": "Annual Purchase", "name": "Annual Purchase",
@@ -150,8 +155,7 @@ def get_number_cards():
["Purchase Order", "transaction_date", "Between", [start_date, end_date], False], ["Purchase Order", "transaction_date", "Between", [start_date, end_date], False],
["Purchase Order", "status", "not in", ["Draft", "Cancelled", "Closed", None], False], ["Purchase Order", "status", "not in", ["Draft", "Cancelled", "Closed", None], False],
["Purchase Order", "docstatus", "=", 1, False], ["Purchase Order", "docstatus", "=", 1, False],
["Purchase Order", "company", "=", company.name, False], ["Purchase Order", "company", "=", company.name, False]
["Purchase Order", "transaction_date", "Between", [start_date,end_date], False]
]), ]),
"function": "Sum", "function": "Sum",
"is_public": 1, "is_public": 1,

View File

@@ -34,9 +34,14 @@
"hidden": 0, "hidden": 0,
"label": "Other Reports", "label": "Other Reports",
"links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
"label": "Regional",
"links": "[\n {\n \"description\": \"Import Italian Purchase Invoices\",\n \"label\": \"Import Supplier Invoice\",\n \"name\": \"Import Supplier Invoice\",\n \"type\": \"doctype\"\n } \n]"
} }
], ],
"cards_label": "Masters & Reports ", "cards_label": "",
"category": "Modules", "category": "Modules",
"charts": [ "charts": [
{ {
@@ -44,7 +49,7 @@
"label": "Purchase Order Trends" "label": "Purchase Order Trends"
} }
], ],
"charts_label": "Buying Dashboard", "charts_label": "",
"creation": "2020-01-28 11:50:26.195467", "creation": "2020-01-28 11:50:26.195467",
"developer_mode_only": 0, "developer_mode_only": 0,
"disable_user_customization": 0, "disable_user_customization": 0,
@@ -55,7 +60,7 @@
"idx": 0, "idx": 0,
"is_standard": 1, "is_standard": 1,
"label": "Buying", "label": "Buying",
"modified": "2020-05-27 19:55:22.407528", "modified": "2020-05-28 13:32:49.960574",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Buying", "name": "Buying",
@@ -104,5 +109,5 @@
"type": "Dashboard" "type": "Dashboard"
} }
], ],
"shortcuts_label": "Quick Access" "shortcuts_label": ""
} }

View File

@@ -11,21 +11,21 @@ frappe.tour['Buying Settings'] = [
{ {
fieldname: "supp_master_name", fieldname: "supp_master_name",
title: "Supplier Naming By", title: "Supplier Naming By",
description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set ") + "<a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a>" + __(" choose the 'Naming Series' option."), description: __("By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a ") + "<a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a>" + __(" choose the 'Naming Series' option."),
}, },
{ {
fieldname: "buying_price_list", fieldname: "buying_price_list",
title: "Default Buying Price List", title: "Default Buying Price List",
description: __("Configure the default Price List when creating a new Buying transaction, the default is set as 'Standard Buying'. Item prices will be fetched from this Price List.") description: __("Configure the default Price List when creating a new Purchase transaction. Item prices will be fetched from this Price List.")
}, },
{ {
fieldname: "po_required", fieldname: "po_required",
title: "Purchase Order Required for Purchase Invoice & Receipt Creation", title: "Purchase Order Required for Purchase Invoice & Receipt Creation",
description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in supplier master.") description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in the Supplier master.")
}, },
{ {
fieldname: "pr_required", fieldname: "pr_required",
title: "Purchase Receipt Required for Purchase Invoice Creation", title: "Purchase Receipt Required for Purchase Invoice Creation",
description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in supplier master.") description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in the Supplier master.")
} }
]; ];

File diff suppressed because it is too large Load Diff

View File

@@ -71,6 +71,15 @@ class PurchaseOrder(BuyingController):
"compare_fields": [["project", "="], ["item_code", "="], "compare_fields": [["project", "="], ["item_code", "="],
["uom", "="], ["conversion_factor", "="]], ["uom", "="], ["conversion_factor", "="]],
"is_child_table": True "is_child_table": True
},
"Material Request": {
"ref_dn_field": "material_request",
"compare_fields": [["company", "="]],
},
"Material Request Item": {
"ref_dn_field": "material_request_item",
"compare_fields": [["project", "="], ["item_code", "="]],
"is_child_table": True
} }
}) })

View File

@@ -185,6 +185,23 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEquals(len(po.get('items')), 1) self.assertEquals(len(po.get('items')), 1)
self.assertEqual(po.status, 'To Receive and Bill') self.assertEqual(po.status, 'To Receive and Bill')
def test_update_child_qty_rate_perm(self):
po = create_purchase_order(item_code= "_Test Item", qty=4)
user = 'test@example.com'
test_user = frappe.get_doc('User', user)
test_user.add_roles("Accounts User")
frappe.set_user(user)
# update qty
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.items[0].name}])
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name)
# add new item
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}])
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name)
frappe.set_user("Administrator")
def test_update_qty(self): def test_update_qty(self):
po = create_purchase_order() po = create_purchase_order()

View File

@@ -25,6 +25,7 @@ class RequestforQuotation(BuyingController):
self.validate_duplicate_supplier() self.validate_duplicate_supplier()
self.validate_supplier_list() self.validate_supplier_list()
validate_for_items(self) validate_for_items(self)
super(RequestforQuotation, self).set_qty_as_per_stock_uom()
self.update_email_id() self.update_email_id()
def validate_duplicate_supplier(self): def validate_duplicate_supplier(self):
@@ -278,6 +279,7 @@ def create_rfq_items(sq_doc, supplier, data):
"description": data.description, "description": data.description,
"qty": data.qty, "qty": data.qty,
"rate": data.rate, "rate": data.rate,
"conversion_factor": data.conversion_factor if data.conversion_factor else None,
"supplier_part_no": frappe.db.get_value("Item Supplier", {'parent': data.item_code, 'supplier': supplier}, "supplier_part_no"), "supplier_part_no": frappe.db.get_value("Item Supplier", {'parent': data.item_code, 'supplier': supplier}, "supplier_part_no"),
"warehouse": data.warehouse or '', "warehouse": data.warehouse or '',
"request_for_quotation_item": data.name, "request_for_quotation_item": data.name,

View File

@@ -6,12 +6,14 @@ from __future__ import unicode_literals
import unittest import unittest
import frappe import frappe
from erpnext.templates.pages.rfq import check_supplier_has_docname_access
from frappe.utils import nowdate from frappe.utils import nowdate
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.templates.pages.rfq import check_supplier_has_docname_access
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation
class TestRequestforQuotation(unittest.TestCase): class TestRequestforQuotation(unittest.TestCase):
def test_quote_status(self): def test_quote_status(self):
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation
rfq = make_request_for_quotation() rfq = make_request_for_quotation()
self.assertEqual(rfq.get('suppliers')[0].quote_status, 'Pending') self.assertEqual(rfq.get('suppliers')[0].quote_status, 'Pending')
@@ -31,7 +33,6 @@ class TestRequestforQuotation(unittest.TestCase):
self.assertEqual(rfq.get('suppliers')[1].quote_status, 'No Quote') self.assertEqual(rfq.get('suppliers')[1].quote_status, 'No Quote')
def test_make_supplier_quotation(self): def test_make_supplier_quotation(self):
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation
rfq = make_request_for_quotation() rfq = make_request_for_quotation()
sq = make_supplier_quotation(rfq.name, rfq.get('suppliers')[0].supplier) sq = make_supplier_quotation(rfq.name, rfq.get('suppliers')[0].supplier)
@@ -51,15 +52,13 @@ class TestRequestforQuotation(unittest.TestCase):
self.assertEqual(sq1.get('items')[0].qty, 5) self.assertEqual(sq1.get('items')[0].qty, 5)
def test_make_supplier_quotation_with_special_characters(self): def test_make_supplier_quotation_with_special_characters(self):
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation
frappe.delete_doc_if_exists("Supplier", "_Test Supplier '1", force=1) frappe.delete_doc_if_exists("Supplier", "_Test Supplier '1", force=1)
supplier = frappe.new_doc("Supplier") supplier = frappe.new_doc("Supplier")
supplier.supplier_name = "_Test Supplier '1" supplier.supplier_name = "_Test Supplier '1"
supplier.supplier_group = "_Test Supplier Group" supplier.supplier_group = "_Test Supplier Group"
supplier.insert() supplier.insert()
rfq = make_request_for_quotation(supplier_wt_appos) rfq = make_request_for_quotation(supplier_data=supplier_wt_appos)
sq = make_supplier_quotation(rfq.name, supplier_wt_appos[0].get("supplier")) sq = make_supplier_quotation(rfq.name, supplier_wt_appos[0].get("supplier"))
sq.submit() sq.submit()
@@ -76,7 +75,6 @@ class TestRequestforQuotation(unittest.TestCase):
frappe.form_dict.name = None frappe.form_dict.name = None
def test_make_supplier_quotation_from_portal(self): def test_make_supplier_quotation_from_portal(self):
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation
rfq = make_request_for_quotation() rfq = make_request_for_quotation()
rfq.get('items')[0].rate = 100 rfq.get('items')[0].rate = 100
rfq.supplier = rfq.suppliers[0].supplier rfq.supplier = rfq.suppliers[0].supplier
@@ -90,12 +88,34 @@ class TestRequestforQuotation(unittest.TestCase):
self.assertEqual(supplier_quotation_doc.get('items')[0].qty, 5) self.assertEqual(supplier_quotation_doc.get('items')[0].qty, 5)
self.assertEqual(supplier_quotation_doc.get('items')[0].amount, 500) self.assertEqual(supplier_quotation_doc.get('items')[0].amount, 500)
def test_make_multi_uom_supplier_quotation(self):
item_code = "_Test Multi UOM RFQ Item"
if not frappe.db.exists('Item', item_code):
item = make_item(item_code, {'stock_uom': '_Test UOM'})
row = item.append('uoms', {
'uom': 'Kg',
'conversion_factor': 2
})
row.db_update()
def make_request_for_quotation(supplier_data=None): rfq = make_request_for_quotation(item_code="_Test Multi UOM RFQ Item", uom="Kg", conversion_factor=2)
rfq.get('items')[0].rate = 100
rfq.supplier = rfq.suppliers[0].supplier
self.assertEqual(rfq.items[0].stock_qty, 10)
supplier_quotation_name = create_supplier_quotation(rfq)
supplier_quotation = frappe.get_doc('Supplier Quotation', supplier_quotation_name)
self.assertEqual(supplier_quotation.items[0].qty, 5)
self.assertEqual(supplier_quotation.items[0].stock_qty, 10)
def make_request_for_quotation(**args):
""" """
:param supplier_data: List containing supplier data :param supplier_data: List containing supplier data
""" """
supplier_data = supplier_data if supplier_data else get_supplier_data() args = frappe._dict(args)
supplier_data = args.get("supplier_data") if args.get("supplier_data") else get_supplier_data()
rfq = frappe.new_doc('Request for Quotation') rfq = frappe.new_doc('Request for Quotation')
rfq.transaction_date = nowdate() rfq.transaction_date = nowdate()
rfq.status = 'Draft' rfq.status = 'Draft'
@@ -106,11 +126,13 @@ def make_request_for_quotation(supplier_data=None):
rfq.append('suppliers', data) rfq.append('suppliers', data)
rfq.append("items", { rfq.append("items", {
"item_code": "_Test Item", "item_code": args.item_code or "_Test Item",
"description": "_Test Item", "description": "_Test Item",
"uom": "_Test UOM", "uom": args.uom or "_Test UOM",
"qty": 5, "stock_uom": args.stock_uom or "_Test UOM",
"warehouse": "_Test Warehouse - _TC", "qty": args.qty or 5,
"conversion_factor": args.conversion_factor or 1.0,
"warehouse": args.warehouse or "_Test Warehouse - _TC",
"schedule_date": nowdate() "schedule_date": nowdate()
}) })

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"autoname": "hash", "autoname": "hash",
"creation": "2016-02-25 08:04:02.452958", "creation": "2016-02-25 08:04:02.452958",
"doctype": "DocType", "doctype": "DocType",
@@ -9,6 +10,7 @@
"supplier_part_no", "supplier_part_no",
"column_break_3", "column_break_3",
"item_name", "item_name",
"schedule_date",
"section_break_5", "section_break_5",
"description", "description",
"item_group", "item_group",
@@ -18,9 +20,11 @@
"image_view", "image_view",
"quantity", "quantity",
"qty", "qty",
"stock_uom",
"col_break2", "col_break2",
"schedule_date",
"uom", "uom",
"conversion_factor",
"stock_qty",
"warehouse_and_reference", "warehouse_and_reference",
"warehouse", "warehouse",
"project_name", "project_name",
@@ -33,7 +37,7 @@
"fields": [ "fields": [
{ {
"bold": 1, "bold": 1,
"columns": 3, "columns": 2,
"fieldname": "item_code", "fieldname": "item_code",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
@@ -98,7 +102,7 @@
{ {
"fieldname": "quantity", "fieldname": "quantity",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Quantity" "label": "Quantity & Stock"
}, },
{ {
"bold": 1, "bold": 1,
@@ -129,12 +133,12 @@
{ {
"fieldname": "uom", "fieldname": "uom",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1,
"label": "UOM", "label": "UOM",
"oldfieldname": "uom", "oldfieldname": "uom",
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "UOM", "options": "UOM",
"print_width": "100px", "print_width": "100px",
"read_only": 1,
"reqd": 1, "reqd": 1,
"width": "100px" "width": "100px"
}, },
@@ -144,7 +148,7 @@
"label": "Warehouse and Reference" "label": "Warehouse and Reference"
}, },
{ {
"columns": 3, "columns": 2,
"fieldname": "warehouse", "fieldname": "warehouse",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
@@ -202,6 +206,7 @@
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"default": "0",
"fieldname": "page_break", "fieldname": "page_break",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Page Break", "label": "Page Break",
@@ -219,10 +224,36 @@
{ {
"fieldname": "section_break_23", "fieldname": "section_break_23",
"fieldtype": "Section Break" "fieldtype": "Section Break"
},
{
"fieldname": "stock_uom",
"fieldtype": "Link",
"label": "Stock UOM",
"options": "UOM",
"print_hide": 1,
"read_only": 1,
"reqd": 1
},
{
"fieldname": "conversion_factor",
"fieldtype": "Float",
"label": "UOM Conversion Factor",
"print_hide": 1,
"read_only": 1,
"reqd": 1
},
{
"fieldname": "stock_qty",
"fieldtype": "Float",
"label": "Qty as per Stock UOM",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
} }
], ],
"istable": 1, "istable": 1,
"modified": "2019-05-01 17:50:23.703801", "links": [],
"modified": "2020-06-12 19:10:36.333441",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Request for Quotation Item", "name": "Request for Quotation Item",

View File

@@ -19,7 +19,7 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying",
"idx": 0, "idx": 0,
"is_complete": 0, "is_complete": 0,
"modified": "2020-05-27 17:17:52.075947", "modified": "2020-06-01 12:55:09.234944",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Buying", "name": "Buying",

View File

@@ -1,19 +1,19 @@
{ {
"action": "Update Settings", "action": "Show Form Tour",
"creation": "2020-05-06 15:53:44.667414", "creation": "2020-05-06 15:53:44.667414",
"docstatus": 0, "docstatus": 0,
"doctype": "Onboarding Step", "doctype": "Onboarding Step",
"idx": 0, "idx": 0,
"is_complete": 0, "is_complete": 0,
"is_mandatory": 0, "is_mandatory": 1,
"is_single": 0, "is_single": 1,
"is_skipped": 0, "is_skipped": 0,
"modified": "2020-05-12 18:30:06.323797", "modified": "2020-06-01 12:52:57.668870",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Buying Settings", "name": "Buying Settings",
"owner": "Administrator", "owner": "Administrator",
"reference_document": "Buying Settings", "reference_document": "Buying Settings",
"show_full_form": 0, "show_full_form": 0,
"title": "Configure Buying Settings.", "title": "Configure Buying Settings.",
"validate_action": 1 "validate_action": 0
} }

View File

@@ -4,6 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import flt
def execute(filters=None): def execute(filters=None):
columns = get_columns(filters) columns = get_columns(filters)
@@ -54,15 +55,16 @@ def get_columns(filters):
"width": 140 "width": 140
}, },
{ {
"label": _("Description"), "label": _("Item"),
"fieldname": "description", "fieldname": "item_code",
"fieldtype": "Data", "fieldtype": "Link",
"width": 200 "options": "Item",
"width": 150
}, },
{ {
"label": _("Quantity"), "label": _("Quantity"),
"fieldname": "quantity", "fieldname": "quantity",
"fieldtype": "Int", "fieldtype": "Float",
"width": 140 "width": 140
}, },
{ {
@@ -118,7 +120,7 @@ def get_columns(filters):
}, },
{ {
"label": _("Purchase Order Amount(Company Currency)"), "label": _("Purchase Order Amount(Company Currency)"),
"fieldname": "purchase_order_amt_usd", "fieldname": "purchase_order_amt_in_company_currency",
"fieldtype": "Float", "fieldtype": "Float",
"width": 140 "width": 140
}, },
@@ -175,17 +177,17 @@ def get_data(filters):
"requesting_site": po.warehouse, "requesting_site": po.warehouse,
"requestor": po.owner, "requestor": po.owner,
"material_request_no": po.material_request, "material_request_no": po.material_request,
"description": po.description, "item_code": po.item_code,
"quantity": po.qty, "quantity": flt(po.qty),
"unit_of_measurement": po.stock_uom, "unit_of_measurement": po.stock_uom,
"status": po.status, "status": po.status,
"purchase_order_date": po.transaction_date, "purchase_order_date": po.transaction_date,
"purchase_order": po.parent, "purchase_order": po.parent,
"supplier": po.supplier, "supplier": po.supplier,
"estimated_cost": mr_record.get('amount'), "estimated_cost": flt(mr_record.get('amount')),
"actual_cost": pi_records.get(po.name), "actual_cost": flt(pi_records.get(po.name)),
"purchase_order_amt": po.amount, "purchase_order_amt": flt(po.amount),
"purchase_order_amt_in_company_currency": po.base_amount, "purchase_order_amt_in_company_currency": flt(po.base_amount),
"expected_delivery_date": po.schedule_date, "expected_delivery_date": po.schedule_date,
"actual_delivery_date": pr_records.get(po.name) "actual_delivery_date": pr_records.get(po.name)
} }
@@ -198,9 +200,14 @@ def get_mapped_mr_details(conditions):
SELECT SELECT
par.transaction_date, par.transaction_date,
par.per_ordered, par.per_ordered,
par.owner,
child.name, child.name,
child.parent, child.parent,
child.amount child.amount,
child.qty,
child.item_code,
child.uom,
par.status
FROM `tabMaterial Request` par, `tabMaterial Request Item` child FROM `tabMaterial Request` par, `tabMaterial Request Item` child
WHERE WHERE
par.per_ordered>=0 par.per_ordered>=0
@@ -217,7 +224,15 @@ def get_mapped_mr_details(conditions):
procurement_record_details = dict( procurement_record_details = dict(
material_request_date=record.transaction_date, material_request_date=record.transaction_date,
material_request_no=record.parent, material_request_no=record.parent,
estimated_cost=record.amount requestor=record.owner,
item_code=record.item_code,
estimated_cost=flt(record.amount),
quantity=flt(record.qty),
unit_of_measurement=record.uom,
status=record.status,
actual_cost=0,
purchase_order_amt=0,
purchase_order_amt_in_company_currency=0
) )
procurement_record_against_mr.append(procurement_record_details) procurement_record_against_mr.append(procurement_record_details)
return mr_records, procurement_record_against_mr return mr_records, procurement_record_against_mr
@@ -259,7 +274,7 @@ def get_po_entries(conditions):
child.warehouse, child.warehouse,
child.material_request, child.material_request,
child.material_request_item, child.material_request_item,
child.description, child.item_code,
child.stock_uom, child.stock_uom,
child.qty, child.qty,
child.amount, child.amount,

View File

@@ -15,7 +15,7 @@ class TestProcurementTracker(unittest.TestCase):
def test_result_for_procurement_tracker(self): def test_result_for_procurement_tracker(self):
filters = { filters = {
'company': '_Test Procurement Company', 'company': '_Test Procurement Company',
'cost_center': '_Test Cost Center - _TC' 'cost_center': 'Main - _TPC'
} }
expected_data = self.generate_expected_data() expected_data = self.generate_expected_data()
report = execute(filters) report = execute(filters)
@@ -33,24 +33,27 @@ class TestProcurementTracker(unittest.TestCase):
country="Pakistan" country="Pakistan"
)).insert() )).insert()
warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company") warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company")
mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse) mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse, cost_center="Main - _TPC")
po = make_purchase_order(mr.name) po = make_purchase_order(mr.name)
po.supplier = "_Test Supplier" po.supplier = "_Test Supplier"
po.get("items")[0].cost_center = "_Test Cost Center - _TC" po.get("items")[0].cost_center = "Main - _TPC"
po.submit() po.submit()
pr = make_purchase_receipt(po.name) pr = make_purchase_receipt(po.name)
pr.get("items")[0].cost_center = "Main - _TPC"
pr.submit() pr.submit()
frappe.db.commit() frappe.db.commit()
date_obj = datetime.date(datetime.now()) date_obj = datetime.date(datetime.now())
po.load_from_db()
expected_data = { expected_data = {
"material_request_date": date_obj, "material_request_date": date_obj,
"cost_center": "_Test Cost Center - _TC", "cost_center": "Main - _TPC",
"project": None, "project": None,
"requesting_site": "_Test Procurement Warehouse - _TPC", "requesting_site": "_Test Procurement Warehouse - _TPC",
"requestor": "Administrator", "requestor": "Administrator",
"material_request_no": mr.name, "material_request_no": mr.name,
"description": '_Test Item 1', "item_code": '_Test Item',
"quantity": 10.0, "quantity": 10.0,
"unit_of_measurement": "_Test UOM", "unit_of_measurement": "_Test UOM",
"status": "To Bill", "status": "To Bill",
@@ -58,9 +61,9 @@ class TestProcurementTracker(unittest.TestCase):
"purchase_order": po.name, "purchase_order": po.name,
"supplier": "_Test Supplier", "supplier": "_Test Supplier",
"estimated_cost": 0.0, "estimated_cost": 0.0,
"actual_cost": None, "actual_cost": 0.0,
"purchase_order_amt": 5000.0, "purchase_order_amt": po.net_total,
"purchase_order_amt_in_company_currency": 300000.0, "purchase_order_amt_in_company_currency": po.base_net_total,
"expected_delivery_date": date_obj, "expected_delivery_date": date_obj,
"actual_delivery_date": date_obj "actual_delivery_date": date_obj
} }

View File

@@ -1,8 +1,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe
from frappe import _ from frappe import _
def get_data(): def get_data():
return [ config = [
{ {
"label": _("Purchasing"), "label": _("Purchasing"),
"icon": "fa fa-star", "icon": "fa fa-star",
@@ -243,3 +244,21 @@ def get_data():
}, },
] ]
regional = {
"label": _("Regional"),
"items": [
{
"type": "doctype",
"name": "Import Supplier Invoice",
"description": _("Import Italian Supplier Invoice."),
"onboard": 1,
}
]
}
countries = frappe.get_all("Company", fields="country")
countries = [country["country"] for country in countries]
if "Italy" in countries:
config.append(regional)
return config

View File

@@ -1137,8 +1137,8 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname,
child_item.item_name = item.item_name child_item.item_name = item.item_name
child_item.description = item.description child_item.description = item.description
child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.uom = item.stock_uom child_item.uom = item.stock_uom
child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
if not child_item.warehouse: if not child_item.warehouse:
frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
@@ -1157,8 +1157,8 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna
child_item.item_name = item.item_name child_item.item_name = item.item_name
child_item.description = item.description child_item.description = item.description
child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.uom = item.stock_uom child_item.uom = item.stock_uom
child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_rate = 1 # Initiallize value will update in parent validation
child_item.base_amount = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation
return child_item return child_item
@@ -1190,6 +1190,26 @@ def check_and_delete_children(parent, data):
@frappe.whitelist() @frappe.whitelist()
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"): def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
def check_permissions(doc, perm_type='create'):
try:
doc.check_permission(perm_type)
except:
action = "add" if perm_type == 'create' else "update"
frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions"))
def get_new_child_item(item_row):
if parent_doctype == "Sales Order":
return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row)
if parent_doctype == "Purchase Order":
return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row)
def validate_quantity(child_item, d):
if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
frappe.throw(_("Cannot set quantity less than delivered quantity"))
if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty):
frappe.throw(_("Cannot set quantity less than received quantity"))
data = json.loads(trans_items) data = json.loads(trans_items)
sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation'] sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
@@ -1201,20 +1221,29 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
new_child_flag = False new_child_flag = False
if not d.get("docname"): if not d.get("docname"):
new_child_flag = True new_child_flag = True
if parent_doctype == "Sales Order": check_permissions(parent, 'create')
child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) child_item = get_new_child_item(d)
if parent_doctype == "Purchase Order":
child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d)
else: else:
check_permissions(parent, 'write')
child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")):
prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate"))
prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty"))
prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(d.get("conversion_factor"))
if parent_doctype == 'Sales Order':
prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date")
elif parent_doctype == 'Purchase Order':
prev_date, new_date = child_item.get("schedule_date"), d.get("schedule_date")
rate_unchanged = prev_rate == new_rate
qty_unchanged = prev_qty == new_qty
conversion_factor_unchanged = prev_con_fac == new_con_fac
date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc
if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged:
continue continue
if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): validate_quantity(child_item, d)
frappe.throw(_("Cannot set quantity less than delivered quantity"))
if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty):
frappe.throw(_("Cannot set quantity less than received quantity"))
child_item.qty = flt(d.get("qty")) child_item.qty = flt(d.get("qty"))
precision = child_item.precision("rate") or 2 precision = child_item.precision("rate") or 2
@@ -1225,6 +1254,18 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
else: else:
child_item.rate = flt(d.get("rate")) child_item.rate = flt(d.get("rate"))
if d.get("conversion_factor"):
if child_item.stock_uom == child_item.uom:
child_item.conversion_factor = 1
else:
child_item.conversion_factor = flt(d.get('conversion_factor'))
if d.get("delivery_date") and parent_doctype == 'Sales Order':
child_item.delivery_date = d.get('delivery_date')
if d.get("schedule_date") and parent_doctype == 'Purchase Order':
child_item.schedule_date = d.get('schedule_date')
if flt(child_item.price_list_rate): if flt(child_item.price_list_rate):
if flt(child_item.rate) > flt(child_item.price_list_rate): if flt(child_item.rate) > flt(child_item.price_list_rate):
# if rate is greater than price_list_rate, set margin # if rate is greater than price_list_rate, set margin

View File

@@ -349,7 +349,7 @@ class BuyingController(StockController):
}) })
if not rm.rate: if not rm.rate:
rm.rate = get_valuation_rate(raw_material_data.item_code, self.supplier_warehouse, rm.rate = get_valuation_rate(raw_material_data.rm_item_code, self.supplier_warehouse,
self.doctype, self.name, currency=self.company_currency, company=self.company) self.doctype, self.name, currency=self.company_currency, company=self.company)
rm.amount = qty * flt(rm.rate) rm.amount = qty * flt(rm.rate)

View File

@@ -70,7 +70,7 @@ def validate_item_variant_attributes(item, args=None):
else: else:
attributes_list = attribute_values.get(attribute.lower(), []) attributes_list = attribute_values.get(attribute.lower(), [])
validate_item_attribute_value(attributes_list, attribute, value, item.name) validate_item_attribute_value(attributes_list, attribute, value, item.name, from_variant=True)
def validate_is_incremental(numeric_attribute, attribute, value, item): def validate_is_incremental(numeric_attribute, attribute, value, item):
from_range = numeric_attribute.from_range from_range = numeric_attribute.from_range
@@ -93,13 +93,20 @@ def validate_is_incremental(numeric_attribute, attribute, value, item):
.format(attribute, from_range, to_range, increment, item), .format(attribute, from_range, to_range, increment, item),
InvalidItemAttributeValueError, title=_('Invalid Attribute')) InvalidItemAttributeValueError, title=_('Invalid Attribute'))
def validate_item_attribute_value(attributes_list, attribute, attribute_value, item): def validate_item_attribute_value(attributes_list, attribute, attribute_value, item, from_variant=True):
allow_rename_attribute_value = frappe.db.get_single_value('Item Variant Settings', 'allow_rename_attribute_value') allow_rename_attribute_value = frappe.db.get_single_value('Item Variant Settings', 'allow_rename_attribute_value')
if allow_rename_attribute_value: if allow_rename_attribute_value:
pass pass
elif attribute_value not in attributes_list: elif attribute_value not in attributes_list:
frappe.throw(_("The value {0} is already assigned to an exisiting Item {2}.").format( if from_variant:
attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Rename Not Allowed')) frappe.throw(_("{0} is not a valid Value for Attribute {1} of Item {2}.").format(
frappe.bold(attribute_value), frappe.bold(attribute), frappe.bold(item)), InvalidItemAttributeValueError, title=_("Invalid Value"))
else:
msg = _("The value {0} is already assigned to an existing Item {1}.").format(
frappe.bold(attribute_value), frappe.bold(item))
msg += "<br>" + _("To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.").format(frappe.bold("Allow Rename Attribute Value"))
frappe.throw(msg, InvalidItemAttributeValueError, title=_('Edit Not Allowed'))
def get_attribute_values(item): def get_attribute_values(item):
if not frappe.flags.attribute_values: if not frappe.flags.attribute_values:

View File

@@ -19,6 +19,7 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass
class StockController(AccountsController): class StockController(AccountsController):
def validate(self): def validate(self):
super(StockController, self).validate() super(StockController, self).validate()
if not self.get('is_return'):
self.validate_inspection() self.validate_inspection()
self.validate_serialized_batch() self.validate_serialized_batch()
self.validate_customer_provided_item() self.validate_customer_provided_item()
@@ -226,7 +227,9 @@ class StockController(AccountsController):
def check_expense_account(self, item): def check_expense_account(self, item):
if not item.get("expense_account"): if not item.get("expense_account"):
frappe.throw(_("Expense Account not set for Item {0}. Please set an Expense Account for the item in the Items table").format(item.item_code)) frappe.throw(_("Row #{0}: Expense Account not set for Item {1}. Please set an Expense \
Account in the Items table").format(item.idx, frappe.bold(item.item_code)),
title=_("Expense Account Missing"))
else: else:
is_expense_account = frappe.db.get_value("Account", is_expense_account = frappe.db.get_value("Account",

View File

@@ -21,8 +21,8 @@ def get_dashboards():
{ "chart": "Opportunity Trends", "width": "Full"}, { "chart": "Opportunity Trends", "width": "Full"},
{ "chart": "Won Opportunities", "width": "Full" }, { "chart": "Won Opportunities", "width": "Full" },
{ "chart": "Territory Wise Opportunity Count", "width": "Half"}, { "chart": "Territory Wise Opportunity Count", "width": "Half"},
{ "chart": "Territory Wise Sales", "width": "Half"},
{ "chart": "Opportunities via Campaigns", "width": "Half" }, { "chart": "Opportunities via Campaigns", "width": "Half" },
{ "chart": "Territory Wise Sales", "width": "Full"},
{ "chart": "Lead Source", "width": "Half"} { "chart": "Lead Source", "width": "Half"}
], ],
"cards": [ "cards": [
@@ -59,7 +59,7 @@ def get_charts():
'is_public': 1, 'is_public': 1,
'timeseries': 1, 'timeseries': 1,
"owner": "Administrator", "owner": "Administrator",
"filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), "filters_json": json.dumps([]),
"type": "Bar" "type": "Bar"
}, },
{ {
@@ -90,7 +90,11 @@ def get_charts():
'timeseries': 1, 'timeseries': 1,
"owner": "Administrator", "owner": "Administrator",
"filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]),
"type": "Pie" "type": "Pie",
"custom_options": json.dumps({
"truncateLegends": 1,
"maxSlices": 8
})
}, },
{ {
"name": "Won Opportunities", "name": "Won Opportunities",
@@ -123,7 +127,11 @@ def get_charts():
["Opportunity", "company", "=", company, False] ["Opportunity", "company", "=", company, False]
]), ]),
"owner": "Administrator", "owner": "Administrator",
"type": "Donut" "type": "Donut",
"custom_options": json.dumps({
"truncateLegends": 1,
"maxSlices": 8
})
}, },
{ {
"name": "Territory Wise Sales", "name": "Territory Wise Sales",
@@ -140,7 +148,7 @@ def get_charts():
["Opportunity", "company", "=", company, False], ["Opportunity", "company", "=", company, False],
["Opportunity", "status", "=", "Converted", False] ["Opportunity", "status", "=", "Converted", False]
]), ]),
"type": "Donut" "type": "Bar"
}, },
{ {
"name": "Lead Source", "name": "Lead Source",
@@ -152,7 +160,11 @@ def get_charts():
"document_type": "Lead", "document_type": "Lead",
'is_public': 1, 'is_public': 1,
"owner": "Administrator", "owner": "Administrator",
"type": "Pie" "type": "Pie",
"custom_options": json.dumps({
"truncateLegends": 1,
"maxSlices": 8
})
}] }]
def get_number_cards(): def get_number_cards():

View File

@@ -42,7 +42,7 @@
"idx": 0, "idx": 0,
"is_standard": 1, "is_standard": 1,
"label": "CRM", "label": "CRM",
"modified": "2020-05-20 12:11:36.250491", "modified": "2020-05-28 13:33:52.906750",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "CRM", "name": "CRM",
@@ -52,6 +52,7 @@
"pin_to_top": 0, "pin_to_top": 0,
"shortcuts": [ "shortcuts": [
{ {
"color": "#ffe8cd",
"format": "{} Open", "format": "{} Open",
"label": "Lead", "label": "Lead",
"link_to": "Lead", "link_to": "Lead",
@@ -59,6 +60,7 @@
"type": "DocType" "type": "DocType"
}, },
{ {
"color": "#cef6d1",
"format": "{} Assigned", "format": "{} Assigned",
"label": "Opportunity", "label": "Opportunity",
"link_to": "Opportunity", "link_to": "Opportunity",
@@ -76,7 +78,7 @@
"type": "Report" "type": "Report"
}, },
{ {
"label": "CRM Dashboard", "label": "Dashboard",
"link_to": "CRM", "link_to": "CRM",
"type": "Dashboard" "type": "Dashboard"
} }

View File

@@ -6,6 +6,7 @@
"creation": "2013-04-10 11:45:37", "creation": "2013-04-10 11:45:37",
"doctype": "DocType", "doctype": "DocType",
"document_type": "Document", "document_type": "Document",
"email_append_to": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"organization_lead", "organization_lead",
@@ -448,7 +449,7 @@
"idx": 5, "idx": 5,
"image_field": "image", "image_field": "image",
"links": [], "links": [],
"modified": "2020-05-11 20:27:45.868960", "modified": "2020-06-18 14:39:41.835416",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Lead", "name": "Lead",
@@ -508,8 +509,10 @@
} }
], ],
"search_fields": "lead_name,lead_owner,status", "search_fields": "lead_name,lead_owner,status",
"sender_field": "email_id",
"show_name_in_global_search": 1, "show_name_in_global_search": 1,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"subject_field": "title",
"title_field": "title" "title_field": "title"
} }

View File

@@ -96,6 +96,7 @@ frappe.ui.form.on("Opportunity", {
}); });
} else { } else {
frm.add_custom_button(__("Reopen"), function() { frm.add_custom_button(__("Reopen"), function() {
frm.set_value("lost_reasons",[])
frm.set_value("status", "Open"); frm.set_value("status", "Open");
frm.save(); frm.save();
}); });

View File

@@ -3,11 +3,7 @@ from frappe import _
def get_data(): def get_data():
return { return {
'fieldname': 'prevdoc_docname', 'fieldname': 'opportunity',
'non_standard_fieldnames': {
'Supplier Quotation': 'opportunity',
'Quotation': 'opportunity'
},
'transactions': [ 'transactions': [
{ {
'items': ['Quotation', 'Supplier Quotation'] 'items': ['Quotation', 'Supplier Quotation']

View File

@@ -30,24 +30,32 @@
"fieldname": "text", "fieldname": "text",
"fieldtype": "Small Text", "fieldtype": "Small Text",
"label": "Tweet", "label": "Tweet",
"mandatory_depends_on": "eval:doc.twitter ==1" "mandatory_depends_on": "eval:doc.twitter ==1",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "image", "fieldname": "image",
"fieldtype": "Attach Image", "fieldtype": "Attach Image",
"label": "Image" "label": "Image",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"default": "0", "default": "0",
"fieldname": "twitter", "fieldname": "twitter",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Twitter" "label": "Twitter",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"default": "0", "default": "0",
"fieldname": "linkedin", "fieldname": "linkedin",
"fieldtype": "Check", "fieldtype": "Check",
"label": "LinkedIn" "label": "LinkedIn",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "amended_from", "fieldname": "amended_from",
@@ -56,13 +64,17 @@
"no_copy": 1, "no_copy": 1,
"options": "Social Media Post", "options": "Social Media Post",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"depends_on": "eval:doc.twitter ==1", "depends_on": "eval:doc.twitter ==1",
"fieldname": "content", "fieldname": "content",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Twitter" "label": "Twitter",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
@@ -70,7 +82,9 @@
"fieldtype": "Select", "fieldtype": "Select",
"label": "Post Status", "label": "Post Status",
"options": "\nScheduled\nPosted\nError", "options": "\nScheduled\nPosted\nError",
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
@@ -78,7 +92,9 @@
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 1, "hidden": 1,
"label": "Twitter Post Id", "label": "Twitter Post Id",
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
@@ -86,68 +102,89 @@
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 1, "hidden": 1,
"label": "LinkedIn Post Id", "label": "LinkedIn Post Id",
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "campaign_name", "fieldname": "campaign_name",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
"label": "Campaign", "label": "Campaign",
"options": "Campaign" "options": "Campaign",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "column_break_6", "fieldname": "column_break_6",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"label": "Share On" "label": "Share On",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "column_break_14", "fieldname": "column_break_14",
"fieldtype": "Column Break" "fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "tweet_preview", "fieldname": "tweet_preview",
"fieldtype": "HTML" "fieldtype": "HTML",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"collapsible": 1, "collapsible": 1,
"depends_on": "eval:doc.linkedin==1", "depends_on": "eval:doc.linkedin==1",
"fieldname": "linkedin_section", "fieldname": "linkedin_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "LinkedIn" "label": "LinkedIn",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"collapsible": 1, "collapsible": 1,
"fieldname": "attachments_section", "fieldname": "attachments_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Attachments" "label": "Attachments",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "linkedin_post", "fieldname": "linkedin_post",
"fieldtype": "Text", "fieldtype": "Text",
"label": "Post", "label": "Post",
"mandatory_depends_on": "eval:doc.linkedin ==1" "mandatory_depends_on": "eval:doc.linkedin ==1",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "column_break_15", "fieldname": "column_break_15",
"fieldtype": "Column Break" "fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"fieldname": "scheduled_time", "fieldname": "scheduled_time",
"fieldtype": "Datetime", "fieldtype": "Datetime",
"label": "Scheduled Time", "label": "Scheduled Time",
"read_only_depends_on": "eval:doc.post_status == \"Posted\"" "read_only_depends_on": "eval:doc.post_status == \"Posted\"",
"show_days": 1,
"show_seconds": 1
} }
], ],
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-04-21 15:10:04.953713", "modified": "2020-06-14 10:31:33.961381",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Social Media Post", "name": "Social Media Post",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
"cancel": 1,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
@@ -157,6 +194,35 @@
"report": 1, "report": 1,
"role": "System Manager", "role": "System Manager",
"share": 1, "share": 1,
"submit": 1,
"write": 1
},
{
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User",
"share": 1,
"submit": 1,
"write": 1
},
{
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"submit": 1,
"write": 1 "write": 1
} }
], ],

View File

@@ -16,7 +16,7 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM",
"idx": 0, "idx": 0,
"is_complete": 0, "is_complete": 0,
"modified": "2020-05-27 11:33:09.941263", "modified": "2020-05-28 21:07:41.278784",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "CRM", "name": "CRM",
@@ -35,8 +35,8 @@
"step": "Create and Send Quotation" "step": "Create and Send Quotation"
} }
], ],
"subtitle": "Lead, Opportunity, Customer and more", "subtitle": "Lead, Opportunity, Customer and more.",
"success_message": "CRM Module is all setup!", "success_message": "CRM Module is all Set Up!",
"title": "Let's Setup Your CRM", "title": "Let's Set Up Your CRM.",
"user_can_dismiss": 1 "user_can_dismiss": 1
} }

View File

@@ -8,7 +8,7 @@
"is_mandatory": 0, "is_mandatory": 0,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2020-05-27 11:30:28.237263", "modified": "2020-05-28 21:07:11.461172",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Create and Send Quotation", "name": "Create and Send Quotation",
"owner": "Administrator", "owner": "Administrator",

View File

@@ -8,7 +8,7 @@
"is_mandatory": 0, "is_mandatory": 0,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2020-05-27 11:30:59.493720", "modified": "2020-05-28 21:07:01.373403",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Create Lead", "name": "Create Lead",
"owner": "Administrator", "owner": "Administrator",

View File

@@ -8,7 +8,7 @@
"is_mandatory": 0, "is_mandatory": 0,
"is_single": 0, "is_single": 0,
"is_skipped": 0, "is_skipped": 0,
"modified": "2020-05-27 11:28:07.452857", "modified": "2020-05-14 17:28:16.448676",
"modified_by": "Administrator", "modified_by": "Administrator",
"name": "Introduction to CRM", "name": "Introduction to CRM",
"owner": "Administrator", "owner": "Administrator",

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"allow_import": 1, "allow_import": 1,
"autoname": "naming_series:", "autoname": "naming_series:",
"creation": "2017-07-18 15:21:21.527136", "creation": "2017-07-18 15:21:21.527136",
@@ -7,6 +8,7 @@
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"fee_structure", "fee_structure",
"posting_date",
"due_date", "due_date",
"naming_series", "naming_series",
"fee_creation_status", "fee_creation_status",
@@ -259,10 +261,18 @@
{ {
"fieldname": "dimension_col_break", "fieldname": "dimension_col_break",
"fieldtype": "Column Break" "fieldtype": "Column Break"
},
{
"default": "Today",
"fieldname": "posting_date",
"fieldtype": "Date",
"label": "Posting Date",
"reqd": 1
} }
], ],
"is_submittable": 1, "is_submittable": 1,
"modified": "2019-05-26 09:10:34.522409", "links": [],
"modified": "2020-05-15 08:39:20.682837",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Education", "module": "Education",
"name": "Fee Schedule", "name": "Fee Schedule",

View File

@@ -87,6 +87,7 @@ def generate_fee(fee_schedule):
} }
} }
}) })
fees_doc.posting_date = doc.posting_date
fees_doc.student = student.student fees_doc.student = student.student
fees_doc.student_name = student.student_name fees_doc.student_name = student.student_name
fees_doc.program = student.program fees_doc.program = student.program

View File

@@ -9,6 +9,14 @@ frappe.ui.form.on('Fee Structure', {
}, },
onload: function(frm) { onload: function(frm) {
frm.set_query("academic_term", function() {
return {
"filters": {
"academic_year": frm.doc.academic_year
}
};
});
frm.set_query("receivable_account", function(doc) { frm.set_query("receivable_account", function(doc) {
return { return {
filters: { filters: {

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
"autoname": "naming_series:", "autoname": "naming_series:",
@@ -11,8 +12,8 @@
"program", "program",
"student_category", "student_category",
"column_break_2", "column_break_2",
"academic_term",
"academic_year", "academic_year",
"academic_term",
"section_break_4", "section_break_4",
"components", "components",
"section_break_6", "section_break_6",
@@ -157,7 +158,8 @@
], ],
"icon": "fa fa-flag", "icon": "fa fa-flag",
"is_submittable": 1, "is_submittable": 1,
"modified": "2019-05-26 09:04:17.765758", "links": [],
"modified": "2020-06-16 15:34:57.295010",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Education", "module": "Education",
"name": "Fee Structure", "name": "Fee Structure",

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"creation": "2015-09-07 14:37:01.886859", "creation": "2015-09-07 14:37:01.886859",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
@@ -16,26 +17,33 @@
"in_list_view": 1, "in_list_view": 1,
"label": "Course", "label": "Course",
"options": "Course", "options": "Course",
"reqd": 1 "reqd": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fetch_from": "course.course_name",
"fieldname": "course_name", "fieldname": "course_name",
"fieldtype": "Data", "fieldtype": "Data",
"in_list_view": 1, "in_list_view": 1,
"label": "Course Name", "label": "Course Name",
"fetch_from": "course.course_name", "read_only": 1,
"read_only":1 "show_days": 1,
"show_seconds": 1
}, },
{ {
"default": "0", "default": "0",
"fieldname": "required", "fieldname": "required",
"fieldtype": "Check", "fieldtype": "Check",
"in_list_view": 1, "in_list_view": 1,
"label": "Mandatory" "label": "Mandatory",
"show_days": 1,
"show_seconds": 1
} }
], ],
"istable": 1, "istable": 1,
"modified": "2019-06-12 12:42:12.845972", "links": [],
"modified": "2020-06-09 18:56:10.213241",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Education", "module": "Education",
"name": "Program Course", "name": "Program Course",

View File

@@ -1,398 +1,119 @@
{ {
"allow_copy": 0, "actions": [],
"allow_guest_to_view": 1, "allow_guest_to_view": 1,
"allow_import": 0,
"allow_rename": 1, "allow_rename": 1,
"autoname": "",
"beta": 0,
"creation": "2016-09-13 03:05:27.154713", "creation": "2016-09-13 03:05:27.154713",
"custom": 0,
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Document", "document_type": "Document",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"title",
"route",
"column_break_3",
"academic_year",
"admission_start_date",
"admission_end_date",
"published",
"enable_admission_application",
"section_break_5",
"program_details",
"introduction"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "title", "fieldname": "title",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "label": "Title"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Title",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "route", "fieldname": "route",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Route", "label": "Route",
"length": 0,
"no_copy": 1, "no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 1 "unique": 1
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "application_form_route",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Application Form Route",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3", "fieldname": "column_break_3",
"fieldtype": "Column Break", "fieldtype": "Column Break"
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "academic_year", "fieldname": "academic_year",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 1, "in_standard_filter": 1,
"label": "Academic Year", "label": "Academic Year",
"length": 0,
"no_copy": 1, "no_copy": 1,
"options": "Academic Year", "options": "Academic Year",
"permlevel": 0, "reqd": 1
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "admission_start_date", "fieldname": "admission_start_date",
"fieldtype": "Date", "fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Admission Start Date", "label": "Admission Start Date",
"length": 0, "no_copy": 1
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "admission_end_date", "fieldname": "admission_end_date",
"fieldtype": "Date", "fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Admission End Date", "label": "Admission End Date",
"length": 0, "no_copy": 1
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0, "default": "0",
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "published", "fieldname": "published",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "label": "Publish on website"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Publish on website",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_5", "fieldname": "section_break_5",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "label": "Eligibility and Details"
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Eligibility and Details",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "program_details", "fieldname": "program_details",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Eligibility and Details", "label": "Eligibility and Details",
"length": 0, "options": "Student Admission Program"
"no_copy": 0,
"options": "Student Admission Program",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "introduction", "fieldname": "introduction",
"fieldtype": "Text Editor", "fieldtype": "Text Editor",
"hidden": 0, "label": "Introduction"
"ignore_user_permissions": 0, },
"ignore_xss_filter": 0, {
"in_filter": 0, "default": "0",
"in_global_search": 0, "fieldname": "enable_admission_application",
"in_list_view": 0, "fieldtype": "Check",
"in_standard_filter": 0, "label": "Enable Admission Application"
"label": "Introduction",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
} }
], ],
"has_web_view": 1, "has_web_view": 1,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_published_field": "published", "is_published_field": "published",
"is_submittable": 0, "links": [],
"issingle": 0, "modified": "2020-06-15 20:18:38.591626",
"istable": 0,
"max_attachments": 0,
"modified": "2017-11-10 18:57:34.570376",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Education", "module": "Education",
"name": "Student Admission", "name": "Student Admission",
"name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 1, "export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Academics User", "role": "Academics User",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0,
"write": 1 "write": 1
} }
], ],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Education", "restrict_to_domain": "Education",
"route": "admissions", "route": "admissions",
"show_name_in_global_search": 1, "show_name_in_global_search": 1,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"title_field": "title", "title_field": "title"
"track_changes": 0,
"track_seen": 0
} }

View File

@@ -43,8 +43,8 @@
<thead> <thead>
<tr class="active"> <tr class="active">
<th style="width: 90px">Program/Std.</th> <th style="width: 90px">Program/Std.</th>
<th style="width: 170px">Minumum Age(DOB)</th> <th style="width: 170px">Minumum Age</th>
<th style="width: 170px">Maximum Age(DOB)</th> <th style="width: 170px">Maximum Age</th>
<th style="width: 100px">Application Fee</th> <th style="width: 100px">Application Fee</th>
</tr> </tr>
</thead> </thead>
@@ -52,8 +52,8 @@
{% for row in program_details %} {% for row in program_details %}
<tr> <tr>
<td>{{ row.program }}</td> <td>{{ row.program }}</td>
<td>{{ row.minimum_age }}</td> <td>{{ row.min_age }}</td>
<td>{{ row.maximum_age }}</td> <td>{{ row.max_age }}</td>
<td>{{ row.application_fee }}</td> <td>{{ row.application_fee }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
@@ -61,12 +61,11 @@
</table> </table>
</div> </div>
{% endif %} {% endif %}
{%- if doc.enable_admission_application -%}
{%- if application_form_route -%}
<br> <br>
<p> <p>
<a class='btn btn-primary' <a class='btn btn-primary'
href='/{{ doc.application_form_route }}?new=1'> href='/student-applicant?new=1&student_admission={{doc.name}}'>
{{ _("Apply Now") }}</a> {{ _("Apply Now") }}</a>
</p> </p>
{% endif %} {% endif %}

View File

@@ -11,7 +11,7 @@ QUnit.test('Test: Student Admission', function(assert) {
{admission_start_date: '2016-04-20'}, {admission_start_date: '2016-04-20'},
{admission_end_date: '2016-05-31'}, {admission_end_date: '2016-05-31'},
{title: '2016-17 Admissions'}, {title: '2016-17 Admissions'},
{application_form_route: 'student-applicant'}, {enable_admission_application: 1},
{introduction: 'Test intro'}, {introduction: 'Test intro'},
{program_details: [ {program_details: [
[ [
@@ -28,7 +28,7 @@ QUnit.test('Test: Student Admission', function(assert) {
assert.ok(cur_frm.doc.admission_start_date == '2016-04-20'); assert.ok(cur_frm.doc.admission_start_date == '2016-04-20');
assert.ok(cur_frm.doc.admission_end_date == '2016-05-31'); assert.ok(cur_frm.doc.admission_end_date == '2016-05-31');
assert.ok(cur_frm.doc.title == '2016-17 Admissions'); assert.ok(cur_frm.doc.title == '2016-17 Admissions');
assert.ok(cur_frm.doc.application_form_route == 'student-applicant'); assert.ok(cur_frm.doc.enable_admission_application == 1);
assert.ok(cur_frm.doc.introduction == 'Test intro'); assert.ok(cur_frm.doc.introduction == 'Test intro');
assert.ok(cur_frm.doc.program_details[0].program == 'Standard Test', 'Program correctly selected'); assert.ok(cur_frm.doc.program_details[0].program == 'Standard Test', 'Program correctly selected');
assert.ok(cur_frm.doc.program_details[0].application_fee == 1000); assert.ok(cur_frm.doc.program_details[0].application_fee == 1000);

View File

@@ -1,237 +1,77 @@
{ {
"allow_copy": 0, "actions": [],
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "",
"beta": 0,
"creation": "2017-09-15 12:59:43.207923", "creation": "2017-09-15 12:59:43.207923",
"custom": 0,
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [
"program",
"min_age",
"max_age",
"column_break_4",
"application_fee",
"applicant_naming_series"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "program", "fieldname": "program",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Program", "label": "Program",
"length": 0,
"no_copy": 0,
"options": "Program", "options": "Program",
"permlevel": 0, "show_days": 1,
"precision": "", "show_seconds": 1
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "minimum_age",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Minimum Age",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "maximum_age",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Maximum Age",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4", "fieldname": "column_break_4",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "show_days": 1,
"ignore_user_permissions": 0, "show_seconds": 1
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "application_fee", "fieldname": "application_fee",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Application Fee", "label": "Application Fee",
"length": 0, "show_days": 1,
"no_copy": 0, "show_seconds": 1
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "applicant_naming_series", "fieldname": "applicant_naming_series",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Naming Series (for Student Applicant)", "label": "Naming Series (for Student Applicant)",
"length": 0, "show_days": 1,
"no_copy": 0, "show_seconds": 1
"permlevel": 0, },
"precision": "", {
"print_hide": 0, "fieldname": "min_age",
"print_hide_if_no_value": 0, "fieldtype": "Int",
"read_only": 0, "in_list_view": 1,
"remember_last_selected_value": 0, "label": "Minimum Age",
"report_hide": 0, "show_days": 1,
"reqd": 0, "show_seconds": 1
"search_index": 0, },
"set_only_once": 0, {
"translatable": 0, "fieldname": "max_age",
"unique": 0 "fieldtype": "Int",
"in_list_view": 1,
"label": "Maximum Age",
"show_days": 1,
"show_seconds": 1
} }
], ],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "links": [],
"modified": "2018-11-04 03:37:17.408427", "modified": "2020-06-10 23:06:30.037404",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Education", "module": "Education",
"name": "Student Admission Program", "name": "Student Admission Program",
"name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"quick_entry": 1, "quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Education", "restrict_to_domain": "Education",
"show_name_in_global_search": 0,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"track_changes": 1, "track_changes": 1
"track_seen": 0,
"track_views": 0
} }

View File

@@ -6,7 +6,7 @@ from __future__ import print_function, unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import getdate from frappe.utils import getdate, add_years, nowdate, date_diff
class StudentApplicant(Document): class StudentApplicant(Document):
def autoname(self): def autoname(self):
@@ -31,6 +31,7 @@ class StudentApplicant(Document):
def validate(self): def validate(self):
self.validate_dates() self.validate_dates()
self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name])) self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name]))
if self.student_admission and self.program and self.date_of_birth: if self.student_admission and self.program and self.date_of_birth:
self.validation_from_student_admission() self.validation_from_student_admission()
@@ -48,16 +49,16 @@ class StudentApplicant(Document):
frappe.throw(_("Please select Student Admission which is mandatory for the paid student applicant")) frappe.throw(_("Please select Student Admission which is mandatory for the paid student applicant"))
def validation_from_student_admission(self): def validation_from_student_admission(self):
student_admission = get_student_admission_data(self.student_admission, self.program) student_admission = get_student_admission_data(self.student_admission, self.program)
# different validation for minimum and maximum age so that either min/max can also work independently. if student_admission and student_admission.min_age and \
if student_admission and student_admission.minimum_age and \ date_diff(nowdate(), add_years(getdate(self.date_of_birth), student_admission.min_age)) < 0:
getdate(student_admission.minimum_age) < getdate(self.date_of_birth): frappe.throw(_("Not eligible for the admission in this program as per Date Of Birth"))
frappe.throw(_("Not eligible for the admission in this program as per DOB"))
if student_admission and student_admission.maximum_age and \ if student_admission and student_admission.max_age and \
getdate(student_admission.maximum_age) > getdate(self.date_of_birth): date_diff(nowdate(), add_years(getdate(self.date_of_birth), student_admission.max_age)) > 0:
frappe.throw(_("Not eligible for the admission in this program as per DOB")) frappe.throw(_("Not eligible for the admission in this program as per Date Of Birth"))
def on_payment_authorized(self, *args, **kwargs): def on_payment_authorized(self, *args, **kwargs):
@@ -65,10 +66,12 @@ class StudentApplicant(Document):
def get_student_admission_data(student_admission, program): def get_student_admission_data(student_admission, program):
student_admission = frappe.db.sql("""select sa.admission_start_date, sa.admission_end_date, student_admission = frappe.db.sql("""select sa.admission_start_date, sa.admission_end_date,
sap.program, sap.minimum_age, sap.maximum_age, sap.applicant_naming_series sap.program, sap.min_age, sap.max_age, sap.applicant_naming_series
from `tabStudent Admission` sa, `tabStudent Admission Program` sap from `tabStudent Admission` sa, `tabStudent Admission Program` sap
where sa.name = sap.parent and sa.name = %s and sap.program = %s""", (student_admission, program), as_dict=1) where sa.name = sap.parent and sa.name = %s and sap.program = %s""", (student_admission, program), as_dict=1)
if student_admission: if student_admission:
return student_admission[0] return student_admission[0]
else: else:

View File

@@ -16,7 +16,7 @@
"is_standard": 1, "is_standard": 1,
"login_required": 1, "login_required": 1,
"max_attachment_size": 0, "max_attachment_size": 0,
"modified": "2017-02-21 05:44:46.022738", "modified": "2020-06-11 22:53:45.875310",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Education", "module": "Education",
"name": "student-applicant", "name": "student-applicant",
@@ -24,12 +24,16 @@
"payment_button_label": "Buy Now", "payment_button_label": "Buy Now",
"published": 1, "published": 1,
"route": "student-applicant", "route": "student-applicant",
"route_to_success_link": 0,
"show_attachments": 0,
"show_in_grid": 0,
"show_sidebar": 1, "show_sidebar": 1,
"sidebar_items": [], "sidebar_items": [],
"success_url": "/student-applicant", "success_url": "/student-applicant",
"title": "Student Applicant", "title": "Student Applicant",
"web_form_fields": [ "web_form_fields": [
{ {
"allow_read_on_all_link_options": 0,
"fieldname": "first_name", "fieldname": "first_name",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@@ -37,9 +41,11 @@
"max_length": 0, "max_length": 0,
"max_value": 0, "max_value": 0,
"read_only": 0, "read_only": 0,
"reqd": 1 "reqd": 1,
"show_in_filter": 0
}, },
{ {
"allow_read_on_all_link_options": 0,
"fieldname": "middle_name", "fieldname": "middle_name",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@@ -47,9 +53,11 @@
"max_length": 0, "max_length": 0,
"max_value": 0, "max_value": 0,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0,
"show_in_filter": 0
}, },
{ {
"allow_read_on_all_link_options": 0,
"fieldname": "last_name", "fieldname": "last_name",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@@ -57,9 +65,11 @@
"max_length": 0, "max_length": 0,
"max_value": 0, "max_value": 0,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0,
"show_in_filter": 0
}, },
{ {
"allow_read_on_all_link_options": 0,
"fieldname": "image", "fieldname": "image",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@@ -67,9 +77,11 @@
"max_length": 0, "max_length": 0,
"max_value": 0, "max_value": 0,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0,
"show_in_filter": 0
}, },
{ {
"allow_read_on_all_link_options": 0,
"fieldname": "program", "fieldname": "program",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@@ -78,9 +90,11 @@
"max_value": 0, "max_value": 0,
"options": "Program", "options": "Program",
"read_only": 0, "read_only": 0,
"reqd": 1 "reqd": 1,
"show_in_filter": 0
}, },
{ {
"allow_read_on_all_link_options": 0,
"fieldname": "academic_year", "fieldname": "academic_year",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@@ -89,9 +103,11 @@
"max_value": 0, "max_value": 0,
"options": "Academic Year", "options": "Academic Year",
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0,
"show_in_filter": 0
}, },
{ {
"allow_read_on_all_link_options": 0,
"fieldname": "date_of_birth", "fieldname": "date_of_birth",
"fieldtype": "Date", "fieldtype": "Date",
"hidden": 0, "hidden": 0,
@@ -99,9 +115,11 @@
"max_length": 0, "max_length": 0,
"max_value": 0, "max_value": 0,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0,
"show_in_filter": 0
}, },
{ {
"allow_read_on_all_link_options": 0,
"fieldname": "blood_group", "fieldname": "blood_group",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 0, "hidden": 0,
@@ -110,9 +128,11 @@
"max_value": 0, "max_value": 0,
"options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-", "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-",
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0,
"show_in_filter": 0
}, },
{ {
"allow_read_on_all_link_options": 0,
"fieldname": "student_email_id", "fieldname": "student_email_id",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@@ -120,9 +140,11 @@
"max_length": 0, "max_length": 0,
"max_value": 0, "max_value": 0,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0,
"show_in_filter": 0
}, },
{ {
"allow_read_on_all_link_options": 0,
"fieldname": "student_mobile_number", "fieldname": "student_mobile_number",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@@ -130,9 +152,11 @@
"max_length": 0, "max_length": 0,
"max_value": 0, "max_value": 0,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0,
"show_in_filter": 0
}, },
{ {
"allow_read_on_all_link_options": 0,
"default": "INDIAN", "default": "INDIAN",
"fieldname": "nationality", "fieldname": "nationality",
"fieldtype": "Data", "fieldtype": "Data",
@@ -142,9 +166,11 @@
"max_value": 0, "max_value": 0,
"options": "", "options": "",
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0,
"show_in_filter": 0
}, },
{ {
"allow_read_on_all_link_options": 0,
"fieldname": "address_line_1", "fieldname": "address_line_1",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@@ -152,9 +178,11 @@
"max_length": 0, "max_length": 0,
"max_value": 0, "max_value": 0,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0,
"show_in_filter": 0
}, },
{ {
"allow_read_on_all_link_options": 0,
"fieldname": "address_line_2", "fieldname": "address_line_2",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@@ -162,9 +190,11 @@
"max_length": 0, "max_length": 0,
"max_value": 0, "max_value": 0,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0,
"show_in_filter": 0
}, },
{ {
"allow_read_on_all_link_options": 0,
"fieldname": "pincode", "fieldname": "pincode",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@@ -172,9 +202,11 @@
"max_length": 0, "max_length": 0,
"max_value": 0, "max_value": 0,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0,
"show_in_filter": 0
}, },
{ {
"allow_read_on_all_link_options": 0,
"fieldname": "guardians", "fieldname": "guardians",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 0, "hidden": 0,
@@ -183,9 +215,11 @@
"max_value": 0, "max_value": 0,
"options": "Student Guardian", "options": "Student Guardian",
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0,
"show_in_filter": 0
}, },
{ {
"allow_read_on_all_link_options": 0,
"fieldname": "siblings", "fieldname": "siblings",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 0, "hidden": 0,
@@ -194,7 +228,21 @@
"max_value": 0, "max_value": 0,
"options": "Student Sibling", "options": "Student Sibling",
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "student_admission",
"fieldtype": "Link",
"hidden": 0,
"label": "Student Admission",
"max_length": 0,
"max_value": 0,
"options": "Student Admission",
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
} }
] ]
} }

View File

@@ -241,12 +241,15 @@ def get_order_taxes(shopify_order, shopify_settings):
return taxes return taxes
def update_taxes_with_shipping_lines(taxes, shipping_lines, shopify_settings): def update_taxes_with_shipping_lines(taxes, shipping_lines, shopify_settings):
"""Shipping lines represents the shipping details,
each such shipping detail consists of a list of tax_lines"""
for shipping_charge in shipping_lines: for shipping_charge in shipping_lines:
for tax in shipping_charge.get("tax_lines"):
taxes.append({ taxes.append({
"charge_type": _("Actual"), "charge_type": _("Actual"),
"account_head": get_tax_account_head(shipping_charge), "account_head": get_tax_account_head(tax),
"description": shipping_charge["title"], "description": tax["title"],
"tax_amount": shipping_charge["price"], "tax_amount": tax["price"],
"cost_center": shopify_settings.cost_center "cost_center": shopify_settings.cost_center
}) })

View File

@@ -73,10 +73,16 @@ def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name
if customer_exists: if customer_exists:
frappe.rename_doc("Customer", old_name, customer_name) frappe.rename_doc("Customer", old_name, customer_name)
billing_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Billing"}) for address_type in ("Billing", "Shipping",):
shipping_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Shipping"}) try:
rename_address(billing_address, customer) address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": address_type})
rename_address(shipping_address, customer) rename_address(address, customer)
except (
frappe.DoesNotExistError,
frappe.DuplicateEntryError,
frappe.ValidationError,
):
pass
else: else:
create_address(raw_billing_data, customer, "Billing") create_address(raw_billing_data, customer, "Billing")
create_address(raw_shipping_data, customer, "Shipping") create_address(raw_shipping_data, customer, "Shipping")

View File

@@ -124,10 +124,11 @@ def add_account_subtype(account_subtype):
@frappe.whitelist() @frappe.whitelist()
def sync_transactions(bank, bank_account): def sync_transactions(bank, bank_account):
'''Sync transactions based on the last integration date as the start date, after the sync is completed
last_sync_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date") add the transaction date of the oldest transaction as the last integration date'''
if last_sync_date: last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date")
start_date = formatdate(last_sync_date, "YYYY-MM-dd") if last_transaction_date:
start_date = formatdate(last_transaction_date, "YYYY-MM-dd")
else: else:
start_date = formatdate(add_months(today(), -12), "YYYY-MM-dd") start_date = formatdate(add_months(today(), -12), "YYYY-MM-dd")
end_date = formatdate(today(), "YYYY-MM-dd") end_date = formatdate(today(), "YYYY-MM-dd")
@@ -139,12 +140,14 @@ def sync_transactions(bank, bank_account):
for transaction in reversed(transactions): for transaction in reversed(transactions):
result += new_bank_transaction(transaction) result += new_bank_transaction(transaction)
if result:
last_transaction_date = frappe.db.get_value('Bank Transaction', result.pop(), 'date')
frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format( frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format(
len(result), bank_account, start_date, end_date)) len(result), bank_account, start_date, end_date))
frappe.db.set_value("Bank Account", bank_account, "last_integration_date", getdate(end_date)) frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date)
return result
except Exception: except Exception:
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error")) frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))

View File

@@ -258,7 +258,7 @@
} }
], ],
"issingle": 1, "issingle": 1,
"modified": "2019-09-13 12:32:11.384757", "modified": "2020-05-28 12:32:11.384757",
"modified_by": "umair@erpnext.com", "modified_by": "umair@erpnext.com",
"module": "ERPNext Integrations", "module": "ERPNext Integrations",
"name": "Shopify Settings", "name": "Shopify Settings",

View File

@@ -8,6 +8,7 @@ import json
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import get_request_session from frappe.utils import get_request_session
from requests.exceptions import HTTPError
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from erpnext.erpnext_integrations.utils import get_webhook_address from erpnext.erpnext_integrations.utils import get_webhook_address
from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log
@@ -29,19 +30,24 @@ class ShopifySettings(Document):
webhooks = ["orders/create", "orders/paid", "orders/fulfilled"] webhooks = ["orders/create", "orders/paid", "orders/fulfilled"]
# url = get_shopify_url('admin/webhooks.json', self) # url = get_shopify_url('admin/webhooks.json', self)
created_webhooks = [d.method for d in self.webhooks] created_webhooks = [d.method for d in self.webhooks]
url = get_shopify_url('admin/api/2019-04/webhooks.json', self) url = get_shopify_url('admin/api/2020-04/webhooks.json', self)
for method in webhooks: for method in webhooks:
session = get_request_session() session = get_request_session()
try: try:
d = session.post(url, data=json.dumps({ res = session.post(url, data=json.dumps({
"webhook": { "webhook": {
"topic": method, "topic": method,
"address": get_webhook_address(connector_name='shopify_connection', method='store_request_data'), "address": get_webhook_address(connector_name='shopify_connection', method='store_request_data'),
"format": "json" "format": "json"
} }
}), headers=get_header(self)) }), headers=get_header(self))
d.raise_for_status() res.raise_for_status()
self.update_webhook_table(method, d.json()) self.update_webhook_table(method, res.json())
except HTTPError as e:
error_message = res.json().get('errors', e)
make_shopify_log(status="Warning", exception=error_message, rollback=True)
except Exception as e: except Exception as e:
make_shopify_log(status="Warning", exception=e, rollback=True) make_shopify_log(status="Warning", exception=e, rollback=True)
@@ -50,13 +56,18 @@ class ShopifySettings(Document):
deleted_webhooks = [] deleted_webhooks = []
for d in self.webhooks: for d in self.webhooks:
url = get_shopify_url('admin/api/2019-04/webhooks/{0}.json'.format(d.webhook_id), self) url = get_shopify_url('admin/api/2020-04/webhooks/{0}.json'.format(d.webhook_id), self)
try: try:
res = session.delete(url, headers=get_header(self)) res = session.delete(url, headers=get_header(self))
res.raise_for_status() res.raise_for_status()
deleted_webhooks.append(d) deleted_webhooks.append(d)
except HTTPError as e:
error_message = res.json().get('errors', e)
make_shopify_log(status="Warning", exception=error_message, rollback=True)
except Exception as e: except Exception as e:
frappe.log_error(message=frappe.get_traceback(), title=e) frappe.log_error(message=e, title='Shopify Webhooks Issue')
for d in deleted_webhooks: for d in deleted_webhooks:
self.remove(d) self.remove(d)
@@ -125,4 +136,3 @@ def setup_custom_fields():
} }
create_custom_fields(custom_fields) create_custom_fields(custom_fields)

View File

@@ -8,7 +8,7 @@ from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings impo
shopify_variants_attr_list = ["option1", "option2", "option3"] shopify_variants_attr_list = ["option1", "option2", "option3"]
def sync_item_from_shopify(shopify_settings, item): def sync_item_from_shopify(shopify_settings, item):
url = get_shopify_url("admin/api/2019-04/products/{0}.json".format(item.get("product_id")), shopify_settings) url = get_shopify_url("admin/api/2020-04/products/{0}.json".format(item.get("product_id")), shopify_settings)
session = get_request_session() session = get_request_session()
try: try:

View File

@@ -1,7 +1,9 @@
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on('Tally Migration', { frappe.provide("erpnext.tally_migration");
frappe.ui.form.on("Tally Migration", {
onload: function (frm) { onload: function (frm) {
let reload_status = true; let reload_status = true;
frappe.realtime.on("tally_migration_progress_update", function (data) { frappe.realtime.on("tally_migration_progress_update", function (data) {
@@ -35,7 +37,17 @@ frappe.ui.form.on('Tally Migration', {
} }
}); });
}, },
refresh: function (frm) { refresh: function (frm) {
frm.trigger("show_logs_preview");
erpnext.tally_migration.failed_import_log = JSON.parse(frm.doc.failed_import_log);
erpnext.tally_migration.fixed_errors_log = JSON.parse(frm.doc.fixed_errors_log);
["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => {
frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1)
frm.toggle_enable(account, frm.doc.is_day_book_data_processed != 1)
})
if (frm.doc.master_data && !frm.doc.is_master_data_imported) { if (frm.doc.master_data && !frm.doc.is_master_data_imported) {
if (frm.doc.is_master_data_processed) { if (frm.doc.is_master_data_processed) {
if (frm.doc.status != "Importing Master Data") { if (frm.doc.status != "Importing Master Data") {
@@ -47,6 +59,7 @@ frappe.ui.form.on('Tally Migration', {
} }
} }
} }
if (frm.doc.day_book_data && !frm.doc.is_day_book_data_imported) { if (frm.doc.day_book_data && !frm.doc.is_day_book_data_imported) {
if (frm.doc.is_day_book_data_processed) { if (frm.doc.is_day_book_data_processed) {
if (frm.doc.status != "Importing Day Book Data") { if (frm.doc.status != "Importing Day Book Data") {
@@ -59,6 +72,17 @@ frappe.ui.form.on('Tally Migration', {
} }
} }
}, },
erpnext_company: function (frm) {
frappe.db.exists("Company", frm.doc.erpnext_company).then(exists => {
if (exists) {
frappe.msgprint(
__("Company {0} already exists. Continuing will overwrite the Company and Chart of Accounts", [frm.doc.erpnext_company]),
);
}
});
},
add_button: function (frm, label, method) { add_button: function (frm, label, method) {
frm.add_custom_button( frm.add_custom_button(
label, label,
@@ -71,5 +95,255 @@ frappe.ui.form.on('Tally Migration', {
frm.reload_doc(); frm.reload_doc();
} }
); );
},
render_html_table(frm, shown_logs, hidden_logs, field) {
if (shown_logs && shown_logs.length > 0) {
frm.toggle_display(field, true);
} else {
frm.toggle_display(field, false);
return
}
let rows = erpnext.tally_migration.get_html_rows(shown_logs, field);
let rows_head, table_caption;
let table_footer = (hidden_logs && (hidden_logs.length > 0)) ? `<tr class="text-muted">
<td colspan="4">And ${hidden_logs.length} more others</td>
</tr>`: "";
if (field === "fixed_error_log_preview") {
rows_head = `<th width="75%">${__("Meta Data")}</th>
<th width="10%">${__("Unresolve")}</th>`
table_caption = "Resolved Issues"
} else {
rows_head = `<th width="75%">${__("Error Message")}</th>
<th width="10%">${__("Create")}</th>`
table_caption = "Error Log"
}
frm.get_field(field).$wrapper.html(`
<table class="table table-bordered">
<caption>${table_caption}</caption>
<tr class="text-muted">
<th width="5%">${__("#")}</th>
<th width="10%">${__("DocType")}</th>
${rows_head}
</tr>
${rows}
${table_footer}
</table>
`);
},
show_error_summary(frm) {
let summary = erpnext.tally_migration.failed_import_log.reduce((summary, row) => {
if (row.doc) {
if (summary[row.doc.doctype]) {
summary[row.doc.doctype] += 1;
} else {
summary[row.doc.doctype] = 1;
}
}
return summary
}, {});
console.table(summary);
},
show_logs_preview(frm) {
let empty = "[]";
let import_log = frm.doc.failed_import_log || empty;
let completed_log = frm.doc.fixed_errors_log || empty;
let render_section = !(import_log === completed_log && import_log === empty);
frm.toggle_display("import_log_section", render_section);
if (render_section) {
frm.trigger("show_error_summary");
frm.trigger("show_errored_import_log");
frm.trigger("show_fixed_errors_log");
}
},
show_errored_import_log(frm) {
let import_log = erpnext.tally_migration.failed_import_log;
let logs = import_log.slice(0, 20);
let hidden_logs = import_log.slice(20);
frm.events.render_html_table(frm, logs, hidden_logs, "failed_import_preview");
},
show_fixed_errors_log(frm) {
let completed_log = erpnext.tally_migration.fixed_errors_log;
let logs = completed_log.slice(0, 20);
let hidden_logs = completed_log.slice(20);
frm.events.render_html_table(frm, logs, hidden_logs, "fixed_error_log_preview");
} }
}); });
erpnext.tally_migration.getError = (traceback) => {
/* Extracts the Error Message from the Python Traceback or Solved error */
let is_multiline = traceback.trim().indexOf("\n") != -1;
let message;
if (is_multiline) {
let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1
let error_line = traceback.substr(exc_error_idx)
let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0;
message = error_line.slice(split_str_idx).trim();
} else {
message = traceback;
}
return message
}
erpnext.tally_migration.cleanDoc = (obj) => {
/* Strips all null and empty values of your JSON object */
let temp = obj;
$.each(temp, function(key, value){
if (value === "" || value === null){
delete obj[key];
} else if (Object.prototype.toString.call(value) === '[object Object]') {
erpnext.tally_migration.cleanDoc(value);
} else if ($.isArray(value)) {
$.each(value, function (k,v) { erpnext.tally_migration.cleanDoc(v); });
}
});
return temp;
}
erpnext.tally_migration.unresolve = (document) => {
/* Mark document migration as unresolved ie. move to failed error log */
let frm = cur_frm;
let failed_log = erpnext.tally_migration.failed_import_log;
let fixed_log = erpnext.tally_migration.fixed_errors_log;
let modified_fixed_log = fixed_log.filter(row => {
if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) {
return row
}
});
failed_log.push({ doc: document, exc: `Marked unresolved on ${Date()}` });
frm.doc.failed_import_log = JSON.stringify(failed_log);
frm.doc.fixed_errors_log = JSON.stringify(modified_fixed_log);
frm.dirty();
frm.save();
}
erpnext.tally_migration.resolve = (document) => {
/* Mark document migration as resolved ie. move to fixed error log */
let frm = cur_frm;
let failed_log = erpnext.tally_migration.failed_import_log;
let fixed_log = erpnext.tally_migration.fixed_errors_log;
let modified_failed_log = failed_log.filter(row => {
if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) {
return row
}
});
fixed_log.push({ doc: document, exc: `Solved on ${Date()}` });
frm.doc.failed_import_log = JSON.stringify(modified_failed_log);
frm.doc.fixed_errors_log = JSON.stringify(fixed_log);
frm.dirty();
frm.save();
}
erpnext.tally_migration.create_new_doc = (document) => {
/* Mark as resolved and create new document */
erpnext.tally_migration.resolve(document);
return frappe.call({
type: "POST",
method: 'erpnext.erpnext_integrations.doctype.tally_migration.tally_migration.new_doc',
args: {
document
},
freeze: true,
callback: function(r) {
if(!r.exc) {
frappe.model.sync(r.message);
frappe.get_doc(r.message.doctype, r.message.name).__run_link_triggers = true;
frappe.set_route("Form", r.message.doctype, r.message.name);
}
}
});
}
erpnext.tally_migration.get_html_rows = (logs, field) => {
let index = 0;
let rows = logs
.map(({ doc, exc }) => {
let id = frappe.dom.get_unique_id();
let traceback = exc;
let error_message = erpnext.tally_migration.getError(traceback);
index++;
let show_traceback = `
<button class="btn btn-default btn-xs m-3" type="button" data-toggle="collapse" data-target="#${id}-traceback" aria-expanded="false" aria-controls="${id}-traceback">
${__("Show Traceback")}
</button>
<div class="collapse margin-top" id="${id}-traceback">
<div class="well">
<pre style="font-size: smaller;">${traceback}</pre>
</div>
</div>`;
let show_doc = `
<button class='btn btn-default btn-xs m-3' type='button' data-toggle='collapse' data-target='#${id}-doc' aria-expanded='false' aria-controls='${id}-doc'>
${__("Show Document")}
</button>
<div class="collapse margin-top" id="${id}-doc">
<div class="well">
<pre style="font-size: smaller;">${JSON.stringify(erpnext.tally_migration.cleanDoc(doc), null, 1)}</pre>
</div>
</div>`;
let create_button = `
<button class='btn btn-default btn-xs m-3' type='button' onclick='erpnext.tally_migration.create_new_doc(${JSON.stringify(doc)})'>
${__("Create Document")}
</button>`
let mark_as_unresolved = `
<button class='btn btn-default btn-xs m-3' type='button' onclick='erpnext.tally_migration.unresolve(${JSON.stringify(doc)})'>
${__("Mark as unresolved")}
</button>`
if (field === "fixed_error_log_preview") {
return `<tr>
<td>${index}</td>
<td>
<div>${doc.doctype}</div>
</td>
<td>
<div>${error_message}</div>
<div>${show_doc}</div>
</td>
<td>
<div>${mark_as_unresolved}</div>
</td>
</tr>`;
} else {
return `<tr>
<td>${index}</td>
<td>
<div>${doc.doctype}</div>
</td>
<td>
<div>${error_message}</div>
<div>${show_traceback}</div>
<div>${show_doc}</div>
</td>
<td>
<div>${create_button}</div>
</td>
</tr>`;
}
}).join("");
return rows
}

View File

@@ -28,14 +28,19 @@
"vouchers", "vouchers",
"accounts_section", "accounts_section",
"default_warehouse", "default_warehouse",
"round_off_account", "default_round_off_account",
"column_break_21", "column_break_21",
"default_cost_center", "default_cost_center",
"day_book_section", "day_book_section",
"day_book_data", "day_book_data",
"column_break_27", "column_break_27",
"is_day_book_data_processed", "is_day_book_data_processed",
"is_day_book_data_imported" "is_day_book_data_imported",
"import_log_section",
"failed_import_log",
"fixed_errors_log",
"failed_import_preview",
"fixed_error_log_preview"
], ],
"fields": [ "fields": [
{ {
@@ -57,6 +62,7 @@
"fieldname": "tally_creditors_account", "fieldname": "tally_creditors_account",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Tally Creditors Account", "label": "Tally Creditors Account",
"read_only_depends_on": "eval:doc.is_master_data_processed==1",
"reqd": 1 "reqd": 1
}, },
{ {
@@ -69,6 +75,7 @@
"fieldname": "tally_debtors_account", "fieldname": "tally_debtors_account",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Tally Debtors Account", "label": "Tally Debtors Account",
"read_only_depends_on": "eval:doc.is_master_data_processed==1",
"reqd": 1 "reqd": 1
}, },
{ {
@@ -136,6 +143,7 @@
}, },
{ {
"depends_on": "is_master_data_imported", "depends_on": "is_master_data_imported",
"description": "The accounts are set by the system automatically but do confirm these defaults",
"fieldname": "accounts_section", "fieldname": "accounts_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Accounts" "label": "Accounts"
@@ -146,12 +154,6 @@
"label": "Default Warehouse", "label": "Default Warehouse",
"options": "Warehouse" "options": "Warehouse"
}, },
{
"fieldname": "round_off_account",
"fieldtype": "Link",
"label": "Round Off Account",
"options": "Account"
},
{ {
"fieldname": "column_break_21", "fieldname": "column_break_21",
"fieldtype": "Column Break" "fieldtype": "Column Break"
@@ -212,11 +214,47 @@
"fieldname": "default_uom", "fieldname": "default_uom",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Default UOM", "label": "Default UOM",
"options": "UOM" "options": "UOM",
"read_only_depends_on": "eval:doc.is_master_data_imported==1"
},
{
"default": "[]",
"fieldname": "failed_import_log",
"fieldtype": "Code",
"hidden": 1,
"options": "JSON"
},
{
"fieldname": "failed_import_preview",
"fieldtype": "HTML",
"label": "Failed Import Log"
},
{
"fieldname": "import_log_section",
"fieldtype": "Section Break",
"label": "Import Log"
},
{
"fieldname": "default_round_off_account",
"fieldtype": "Link",
"label": "Default Round Off Account",
"options": "Account"
},
{
"default": "[]",
"fieldname": "fixed_errors_log",
"fieldtype": "Code",
"hidden": 1,
"options": "JSON"
},
{
"fieldname": "fixed_error_log_preview",
"fieldtype": "HTML",
"label": "Fixed Error Log"
} }
], ],
"links": [], "links": [],
"modified": "2020-04-16 13:03:28.894919", "modified": "2020-04-28 00:29:18.039826",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "ERPNext Integrations", "module": "ERPNext Integrations",
"name": "Tally Migration", "name": "Tally Migration",

View File

@@ -6,6 +6,7 @@ from __future__ import unicode_literals
import json import json
import re import re
import sys
import traceback import traceback
import zipfile import zipfile
from decimal import Decimal from decimal import Decimal
@@ -15,18 +16,34 @@ from bs4 import BeautifulSoup as bs
import frappe import frappe
from erpnext import encode_company_abbr from erpnext import encode_company_abbr
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts
from erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer import unset_existing_data
from frappe import _ from frappe import _
from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.custom.doctype.custom_field.custom_field import create_custom_field
from frappe.model.document import Document from frappe.model.document import Document
from frappe.model.naming import getseries, revert_series_if_last from frappe.model.naming import getseries, revert_series_if_last
from frappe.utils.data import format_datetime from frappe.utils.data import format_datetime
PRIMARY_ACCOUNT = "Primary" PRIMARY_ACCOUNT = "Primary"
VOUCHER_CHUNK_SIZE = 500 VOUCHER_CHUNK_SIZE = 500
@frappe.whitelist()
def new_doc(document):
document = json.loads(document)
doctype = document.pop("doctype")
document.pop("name", None)
doc = frappe.new_doc(doctype)
doc.update(document)
return doc
class TallyMigration(Document): class TallyMigration(Document):
def validate(self):
failed_import_log = json.loads(self.failed_import_log)
sorted_failed_import_log = sorted(failed_import_log, key=lambda row: row["doc"]["creation"])
self.failed_import_log = json.dumps(sorted_failed_import_log)
def autoname(self): def autoname(self):
if not self.name: if not self.name:
self.name = "Tally Migration on " + format_datetime(self.creation) self.name = "Tally Migration on " + format_datetime(self.creation)
@@ -65,9 +82,17 @@ class TallyMigration(Document):
"attached_to_name": self.name, "attached_to_name": self.name,
"content": json.dumps(value), "content": json.dumps(value),
"is_private": True "is_private": True
}).insert() })
try:
f.insert()
except frappe.DuplicateEntryError:
pass
setattr(self, key, f.file_url) setattr(self, key, f.file_url)
def set_account_defaults(self):
self.default_cost_center, self.default_round_off_account = frappe.db.get_value("Company", self.erpnext_company, ["cost_center", "round_off_account"])
self.default_warehouse = frappe.db.get_value("Stock Settings", "Stock Settings", "default_warehouse")
def _process_master_data(self): def _process_master_data(self):
def get_company_name(collection): def get_company_name(collection):
return collection.find_all("REMOTECMPINFO.LIST")[0].REMOTECMPNAME.string.strip() return collection.find_all("REMOTECMPINFO.LIST")[0].REMOTECMPNAME.string.strip()
@@ -84,7 +109,11 @@ class TallyMigration(Document):
children, parents = get_children_and_parent_dict(accounts) children, parents = get_children_and_parent_dict(accounts)
group_set = [acc[1] for acc in accounts if acc[2]] group_set = [acc[1] for acc in accounts if acc[2]]
children, customers, suppliers = remove_parties(parents, children, group_set) children, customers, suppliers = remove_parties(parents, children, group_set)
try:
coa = traverse({}, children, roots, roots, group_set) coa = traverse({}, children, roots, roots, group_set)
except RecursionError:
self.log(_("Error occured while parsing Chart of Accounts: Please make sure that no two accounts have the same name"))
for account in coa: for account in coa:
coa[account]["root_type"] = root_type_map[account] coa[account]["root_type"] = root_type_map[account]
@@ -126,14 +155,18 @@ class TallyMigration(Document):
def remove_parties(parents, children, group_set): def remove_parties(parents, children, group_set):
customers, suppliers = set(), set() customers, suppliers = set(), set()
for account in parents: for account in parents:
found = False
if self.tally_creditors_account in parents[account]: if self.tally_creditors_account in parents[account]:
children.pop(account, None) found = True
if account not in group_set: if account not in group_set:
suppliers.add(account) suppliers.add(account)
elif self.tally_debtors_account in parents[account]: if self.tally_debtors_account in parents[account]:
children.pop(account, None) found = True
if account not in group_set: if account not in group_set:
customers.add(account) customers.add(account)
if found:
children.pop(account, None)
return children, customers, suppliers return children, customers, suppliers
def traverse(tree, children, accounts, roots, group_set): def traverse(tree, children, accounts, roots, group_set):
@@ -151,6 +184,7 @@ class TallyMigration(Document):
parties, addresses = [], [] parties, addresses = [], []
for account in collection.find_all("LEDGER"): for account in collection.find_all("LEDGER"):
party_type = None party_type = None
links = []
if account.NAME.string.strip() in customers: if account.NAME.string.strip() in customers:
party_type = "Customer" party_type = "Customer"
parties.append({ parties.append({
@@ -161,7 +195,9 @@ class TallyMigration(Document):
"territory": "All Territories", "territory": "All Territories",
"customer_type": "Individual", "customer_type": "Individual",
}) })
elif account.NAME.string.strip() in suppliers: links.append({"link_doctype": party_type, "link_name": account["NAME"]})
if account.NAME.string.strip() in suppliers:
party_type = "Supplier" party_type = "Supplier"
parties.append({ parties.append({
"doctype": party_type, "doctype": party_type,
@@ -170,6 +206,8 @@ class TallyMigration(Document):
"supplier_group": "All Supplier Groups", "supplier_group": "All Supplier Groups",
"supplier_type": "Individual", "supplier_type": "Individual",
}) })
links.append({"link_doctype": party_type, "link_name": account["NAME"]})
if party_type: if party_type:
address = "\n".join([a.string.strip() for a in account.find_all("ADDRESS")]) address = "\n".join([a.string.strip() for a in account.find_all("ADDRESS")])
addresses.append({ addresses.append({
@@ -183,7 +221,7 @@ class TallyMigration(Document):
"mobile": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None, "mobile": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None,
"phone": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None, "phone": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None,
"gstin": account.PARTYGSTIN.string.strip() if account.PARTYGSTIN else None, "gstin": account.PARTYGSTIN.string.strip() if account.PARTYGSTIN else None,
"links": [{"link_doctype": party_type, "link_name": account["NAME"]}], "links": links
}) })
return parties, addresses return parties, addresses
@@ -242,12 +280,18 @@ class TallyMigration(Document):
def create_company_and_coa(coa_file_url): def create_company_and_coa(coa_file_url):
coa_file = frappe.get_doc("File", {"file_url": coa_file_url}) coa_file = frappe.get_doc("File", {"file_url": coa_file_url})
frappe.local.flags.ignore_chart_of_accounts = True frappe.local.flags.ignore_chart_of_accounts = True
try:
company = frappe.get_doc({ company = frappe.get_doc({
"doctype": "Company", "doctype": "Company",
"company_name": self.erpnext_company, "company_name": self.erpnext_company,
"default_currency": "INR", "default_currency": "INR",
"enable_perpetual_inventory": 0, "enable_perpetual_inventory": 0,
}).insert() }).insert()
except frappe.DuplicateEntryError:
company = frappe.get_doc("Company", self.erpnext_company)
unset_existing_data(self.erpnext_company)
frappe.local.flags.ignore_chart_of_accounts = False frappe.local.flags.ignore_chart_of_accounts = False
create_charts(company.name, custom_chart=json.loads(coa_file.get_content())) create_charts(company.name, custom_chart=json.loads(coa_file.get_content()))
company.create_default_warehouses() company.create_default_warehouses()
@@ -256,36 +300,35 @@ class TallyMigration(Document):
parties_file = frappe.get_doc("File", {"file_url": parties_file_url}) parties_file = frappe.get_doc("File", {"file_url": parties_file_url})
for party in json.loads(parties_file.get_content()): for party in json.loads(parties_file.get_content()):
try: try:
frappe.get_doc(party).insert() party_doc = frappe.get_doc(party)
party_doc.insert()
except: except:
self.log(party) self.log(party_doc)
addresses_file = frappe.get_doc("File", {"file_url": addresses_file_url}) addresses_file = frappe.get_doc("File", {"file_url": addresses_file_url})
for address in json.loads(addresses_file.get_content()): for address in json.loads(addresses_file.get_content()):
try: try:
frappe.get_doc(address).insert(ignore_mandatory=True) address_doc = frappe.get_doc(address)
address_doc.insert(ignore_mandatory=True)
except: except:
try: self.log(address_doc)
gstin = address.pop("gstin", None)
frappe.get_doc(address).insert(ignore_mandatory=True)
self.log({"address": address, "message": "Invalid GSTIN: {}. Address was created without GSTIN".format(gstin)})
except:
self.log(address)
def create_items_uoms(items_file_url, uoms_file_url): def create_items_uoms(items_file_url, uoms_file_url):
uoms_file = frappe.get_doc("File", {"file_url": uoms_file_url}) uoms_file = frappe.get_doc("File", {"file_url": uoms_file_url})
for uom in json.loads(uoms_file.get_content()): for uom in json.loads(uoms_file.get_content()):
if not frappe.db.exists(uom): if not frappe.db.exists(uom):
try: try:
frappe.get_doc(uom).insert() uom_doc = frappe.get_doc(uom)
uom_doc.insert()
except: except:
self.log(uom) self.log(uom_doc)
items_file = frappe.get_doc("File", {"file_url": items_file_url}) items_file = frappe.get_doc("File", {"file_url": items_file_url})
for item in json.loads(items_file.get_content()): for item in json.loads(items_file.get_content()):
try: try:
frappe.get_doc(item).insert() item_doc = frappe.get_doc(item)
item_doc.insert()
except: except:
self.log(item) self.log(item_doc)
try: try:
self.publish("Import Master Data", _("Creating Company and Importing Chart of Accounts"), 1, 4) self.publish("Import Master Data", _("Creating Company and Importing Chart of Accounts"), 1, 4)
@@ -299,10 +342,13 @@ class TallyMigration(Document):
self.publish("Import Master Data", _("Done"), 4, 4) self.publish("Import Master Data", _("Done"), 4, 4)
self.set_account_defaults()
self.is_master_data_imported = 1 self.is_master_data_imported = 1
frappe.db.commit()
except: except:
self.publish("Import Master Data", _("Process Failed"), -1, 5) self.publish("Import Master Data", _("Process Failed"), -1, 5)
frappe.db.rollback()
self.log() self.log()
finally: finally:
@@ -323,7 +369,9 @@ class TallyMigration(Document):
processed_voucher = function(voucher) processed_voucher = function(voucher)
if processed_voucher: if processed_voucher:
vouchers.append(processed_voucher) vouchers.append(processed_voucher)
frappe.db.commit()
except: except:
frappe.db.rollback()
self.log(voucher) self.log(voucher)
return vouchers return vouchers
@@ -349,6 +397,7 @@ class TallyMigration(Document):
journal_entry = { journal_entry = {
"doctype": "Journal Entry", "doctype": "Journal Entry",
"tally_guid": voucher.GUID.string.strip(), "tally_guid": voucher.GUID.string.strip(),
"tally_voucher_no": voucher.VOUCHERNUMBER.string.strip() if voucher.VOUCHERNUMBER else "",
"posting_date": voucher.DATE.string.strip(), "posting_date": voucher.DATE.string.strip(),
"company": self.erpnext_company, "company": self.erpnext_company,
"accounts": accounts, "accounts": accounts,
@@ -377,6 +426,7 @@ class TallyMigration(Document):
"doctype": doctype, "doctype": doctype,
party_field: voucher.PARTYNAME.string.strip(), party_field: voucher.PARTYNAME.string.strip(),
"tally_guid": voucher.GUID.string.strip(), "tally_guid": voucher.GUID.string.strip(),
"tally_voucher_no": voucher.VOUCHERNUMBER.string.strip() if voucher.VOUCHERNUMBER else "",
"posting_date": voucher.DATE.string.strip(), "posting_date": voucher.DATE.string.strip(),
"due_date": voucher.DATE.string.strip(), "due_date": voucher.DATE.string.strip(),
"items": get_voucher_items(voucher, doctype), "items": get_voucher_items(voucher, doctype),
@@ -468,13 +518,20 @@ class TallyMigration(Document):
oldest_year = new_year oldest_year = new_year
def create_custom_fields(doctypes): def create_custom_fields(doctypes):
for doctype in doctypes: tally_guid_df = {
df = {
"fieldtype": "Data", "fieldtype": "Data",
"fieldname": "tally_guid", "fieldname": "tally_guid",
"read_only": 1, "read_only": 1,
"label": "Tally GUID" "label": "Tally GUID"
} }
tally_voucher_no_df = {
"fieldtype": "Data",
"fieldname": "tally_voucher_no",
"read_only": 1,
"label": "Tally Voucher Number"
}
for df in [tally_guid_df, tally_voucher_no_df]:
for doctype in doctypes:
create_custom_field(doctype, df) create_custom_field(doctype, df)
def create_price_list(): def create_price_list():
@@ -490,7 +547,7 @@ class TallyMigration(Document):
try: try:
frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable") frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable")
frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable") frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable")
frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.round_off_account) frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.default_round_off_account)
vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers}) vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers})
vouchers = json.loads(vouchers_file.get_content()) vouchers = json.loads(vouchers_file.get_content())
@@ -521,11 +578,14 @@ class TallyMigration(Document):
for index, voucher in enumerate(chunk, start=start): for index, voucher in enumerate(chunk, start=start):
try: try:
doc = frappe.get_doc(voucher).insert() voucher_doc = frappe.get_doc(voucher)
doc.submit() voucher_doc.insert()
voucher_doc.submit()
self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total) self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total)
frappe.db.commit()
except: except:
self.log(voucher) frappe.db.rollback()
self.log(voucher_doc)
if is_last: if is_last:
self.status = "" self.status = ""
@@ -551,6 +611,19 @@ class TallyMigration(Document):
frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600) frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600)
def log(self, data=None): def log(self, data=None):
if isinstance(data, frappe.model.document.Document):
if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError:
failed_import_log = json.loads(self.failed_import_log)
doc = data.as_dict()
failed_import_log.append({
"doc": doc,
"exc": traceback.format_exc()
})
self.failed_import_log = json.dumps(failed_import_log, separators=(',', ':'))
self.save()
frappe.db.commit()
else:
data = data or self.status data = data or self.status
message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()])
return frappe.log_error(title="Tally Migration Error", message=message) return frappe.log_error(title="Tally Migration Error", message=message)

View File

@@ -71,7 +71,7 @@ def get_charts():
"chart_name": _("Department wise Patient Appointments"), "chart_name": _("Department wise Patient Appointments"),
"chart_type": "Custom", "chart_type": "Custom",
"source": "Department wise Patient Appointments", "source": "Department wise Patient Appointments",
"filters_json": json.dumps({}), "filters_json": json.dumps([]),
'is_public': 1, 'is_public': 1,
"owner": "Administrator", "owner": "Administrator",
"type": "Bar", "type": "Bar",
@@ -159,7 +159,7 @@ def get_charts():
"document_type": "Patient Encounter Symptom", "document_type": "Patient Encounter Symptom",
"group_by_type": "Count", "group_by_type": "Count",
"group_by_based_on": "complaint", "group_by_based_on": "complaint",
"filters_json": json.dumps({}), "filters_json": json.dumps([]),
'is_public': 1, 'is_public': 1,
"owner": "Administrator", "owner": "Administrator",
"type": "Percentage", "type": "Percentage",
@@ -173,7 +173,7 @@ def get_charts():
"document_type": "Patient Encounter Diagnosis", "document_type": "Patient Encounter Diagnosis",
"group_by_type": "Count", "group_by_type": "Count",
"group_by_based_on": "diagnosis", "group_by_based_on": "diagnosis",
"filters_json": json.dumps({}), "filters_json": json.dumps([]),
'is_public': 1, 'is_public': 1,
"owner": "Administrator", "owner": "Administrator",
"type": "Percentage", "type": "Percentage",
@@ -229,7 +229,7 @@ def get_number_cards():
}, },
{ {
"name": "Appointments to Bill", "name": "Appointments to Bill",
"label": _("Appointments to Bill"), "label": _("Appointments To Bill"),
"function": "Count", "function": "Count",
"doctype": "Number Card", "doctype": "Number Card",
"document_type": "Patient Appointment", "document_type": "Patient Appointment",

View File

@@ -64,7 +64,7 @@
"idx": 0, "idx": 0,
"is_standard": 1, "is_standard": 1,
"label": "Healthcare", "label": "Healthcare",
"modified": "2020-05-19 20:57:22.797267", "modified": "2020-05-28 19:02:28.824995",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Healthcare", "name": "Healthcare",
@@ -109,7 +109,7 @@
"type": "Page" "type": "Page"
}, },
{ {
"label": "Healthcare Dashboard", "label": "Dashboard",
"link_to": "Healthcare", "link_to": "Healthcare",
"type": "Dashboard" "type": "Dashboard"
} }

Some files were not shown because too many files have changed in this diff Show More