Merge branch 'develop' of https://github.com/frappe/erpnext into enable-discount-accounting
diff --git a/.github/helper/semgrep_rules/security.yml b/.github/helper/semgrep_rules/security.yml
index 5a5098b..8b21979 100644
--- a/.github/helper/semgrep_rules/security.yml
+++ b/.github/helper/semgrep_rules/security.yml
@@ -8,18 +8,3 @@
dynamic content. Avoid it or use safe_eval().
languages: [python]
severity: ERROR
-
-- id: frappe-sqli-format-strings
- patterns:
- - pattern-inside: |
- @frappe.whitelist()
- def $FUNC(...):
- ...
- - pattern-either:
- - pattern: frappe.db.sql("..." % ...)
- - pattern: frappe.db.sql(f"...", ...)
- - pattern: frappe.db.sql("...".format(...), ...)
- message: |
- Detected use of raw string formatting for SQL queries. This can lead to sql injection vulnerabilities. Refer security guidelines - https://github.com/frappe/erpnext/wiki/Code-Security-Guidelines
- languages: [python]
- severity: WARNING
diff --git a/.github/stale.yml b/.github/stale.yml
index dabc66eb..9322ae8 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -1,11 +1,11 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
-daysUntilStale: 30
+daysUntilStale: 15
# Number of days of inactivity before a stale Issue or Pull Request is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
-daysUntilClose: 7
+daysUntilClose: 3
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
index 7c6b843..bd62227 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -1,16 +1,26 @@
name: Backport
on:
- pull_request:
+ pull_request_target:
types:
- closed
- labeled
jobs:
- backport:
- runs-on: ubuntu-18.04
- name: Backport
+ main:
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
steps:
- - name: Backport
- uses: tibdex/backport@v1
+ - name: Checkout Actions
+ uses: actions/checkout@v2
with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
+ repository: "frappe/backport"
+ path: ./actions
+ ref: develop
+ - name: Install Actions
+ run: npm install --production --prefix ./actions
+ - name: Run backport
+ uses: ./actions/backport
+ with:
+ token: ${{secrets.BACKPORT_BOT_TOKEN}}
+ labelsToAdd: "backport"
+ title: "{{originalTitle}}"
diff --git a/.github/workflows/docs-checker.yml b/.github/workflows/docs-checker.yml
index cdf676d..db46c56 100644
--- a/.github/workflows/docs-checker.yml
+++ b/.github/workflows/docs-checker.yml
@@ -6,6 +6,7 @@
jobs:
build:
runs-on: ubuntu-latest
+ timeout-minutes: 10
steps:
- name: 'Setup Environment'
diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml
index b96a3d6..72d4028 100644
--- a/.github/workflows/patch.yml
+++ b/.github/workflows/patch.yml
@@ -1,10 +1,17 @@
name: Patch
-on: [pull_request, workflow_dispatch]
+on:
+ pull_request:
+ paths-ignore:
+ - '**.js'
+ - '**.md'
+ workflow_dispatch:
+
jobs:
test:
runs-on: ubuntu-18.04
+ timeout-minutes: 60
name: Patch Test
diff --git a/.github/workflows/server-tests.yml b/.github/workflows/server-tests.yml
index 69afa15..3a1ecd3 100644
--- a/.github/workflows/server-tests.yml
+++ b/.github/workflows/server-tests.yml
@@ -2,13 +2,20 @@
on:
pull_request:
+ paths-ignore:
+ - '**.js'
+ - '**.md'
workflow_dispatch:
push:
branches: [ develop ]
+ paths-ignore:
+ - '**.js'
+ - '**.md'
jobs:
test:
runs-on: ubuntu-18.04
+ timeout-minutes: 60
strategy:
fail-fast: false
diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml
index 412a05b..3959268 100644
--- a/.github/workflows/ui-tests.yml
+++ b/.github/workflows/ui-tests.yml
@@ -2,11 +2,14 @@
on:
pull_request:
+ paths-ignore:
+ - '**.md'
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-18.04
+ timeout-minutes: 60
strategy:
fail-fast: false
diff --git a/CODEOWNERS b/CODEOWNERS
index 219b6bb..a4a14de 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -21,13 +21,13 @@
erpnext/shopping_cart/ @marination
erpnext/stock/ @marination @rohitwaghchaure @ankush
-erpnext/crm/ @ruchamahabal
-erpnext/education/ @ruchamahabal
-erpnext/healthcare/ @ruchamahabal
-erpnext/hr/ @ruchamahabal
+erpnext/crm/ @ruchamahabal @pateljannat
+erpnext/education/ @ruchamahabal @pateljannat
+erpnext/healthcare/ @ruchamahabal @pateljannat @chillaranand
+erpnext/hr/ @ruchamahabal @pateljannat
erpnext/non_profit/ @ruchamahabal
-erpnext/payroll @ruchamahabal
-erpnext/projects/ @ruchamahabal
+erpnext/payroll @ruchamahabal @pateljannat
+erpnext/projects/ @ruchamahabal @pateljannat
erpnext/controllers @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 1166549..c90e01c 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -5,7 +5,7 @@
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
-__version__ = '13.7.0'
+__version__ = '13.8.0'
def get_default_company(user=None):
'''Get default company for user'''
diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py
index 1be2fbf..f763df0 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -230,7 +230,7 @@
if self.check_gle_exists():
throw(_("Account with existing transaction can not be converted to group."))
elif self.account_type and not self.flags.exclude_account_type_check:
- throw(_("Cannot covert to Group because Account Type is selected."))
+ throw(_("Cannot convert to Group because Account Type is selected."))
else:
self.is_group = 1
self.save()
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index 49a2afe..9adce3c 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -10,6 +10,7 @@
"accounts_transactions_settings_section",
"over_billing_allowance",
"role_allowed_to_over_bill",
+ "credit_controller",
"make_payment_via_journal_entry",
"column_break_11",
"check_supplier_invoice_uniqueness",
@@ -28,7 +29,6 @@
"acc_frozen_upto",
"frozen_accounts_modifier",
"column_break_4",
- "credit_controller",
"deferred_accounting_settings_section",
"book_deferred_entries_based_on",
"column_break_18",
@@ -74,11 +74,10 @@
"fieldtype": "Column Break"
},
{
- "description": "This role is allowed to submit transactions that exceed credit limits",
"fieldname": "credit_controller",
"fieldtype": "Link",
"in_list_view": 1,
- "label": "Credit Controller",
+ "label": "Role allowed to bypass Credit Limit",
"options": "Role"
},
{
@@ -276,7 +275,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2021-07-12 18:54:29.084958",
+ "modified": "2021-08-09 13:08:01.335416",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py
index 603e21e..6c25f00 100644
--- a/erpnext/accounts/doctype/budget/test_budget.py
+++ b/erpnext/accounts/doctype/budget/test_budget.py
@@ -249,7 +249,7 @@
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
if budget_against_field == "project":
- budget_against = "_Test Project"
+ budget_against = frappe.db.get_value("Project", {"project_name": "_Test Project"})
else:
budget_against = budget_against_CC or "_Test Cost Center - _TC"
@@ -275,7 +275,7 @@
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
elif budget_against_field == "project":
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date=nowdate())
+ "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project=budget_against, posting_date=nowdate())
def make_budget(**args):
args = frappe._dict(args)
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
index 4fd8413..8456b49 100644
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
@@ -391,5 +391,5 @@
})
company.save()
- install_country_fixtures(company.name)
+ install_country_fixtures(company.name, company.country)
company.create_default_tax_template()
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
index 5619321..f2b0a8c 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
@@ -27,6 +27,9 @@
if not (self.company and self.posting_date):
frappe.throw(_("Please select Company and Posting Date to getting entries"))
+ def on_cancel(self):
+ self.ignore_linked_doctypes = ('GL Entry')
+
@frappe.whitelist()
def check_journal_entry_condition(self):
total_debit = frappe.db.get_value("Journal Entry Account", {
@@ -99,10 +102,12 @@
sum(debit) - sum(credit) as balance
from `tabGL Entry`
where account in (%s)
- group by account, party_type, party
+ and posting_date <= %s
+ and is_cancelled = 0
+ group by account, NULLIF(party_type,''), NULLIF(party,'')
having sum(debit) != sum(credit)
order by account
- """ % ', '.join(['%s']*len(accounts)), tuple(accounts), as_dict=1)
+ """ % (', '.join(['%s']*len(accounts)), '%s'), tuple(accounts + [self.posting_date]), as_dict=1)
return account_details
@@ -143,9 +148,9 @@
"party_type": d.get("party_type"),
"party": d.get("party"),
"account_currency": d.get("account_currency"),
- "balance": d.get("balance_in_account_currency"),
- dr_or_cr: abs(d.get("balance_in_account_currency")),
- "exchange_rate":d.get("new_exchange_rate"),
+ "balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")),
+ dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")),
+ "exchange_rate": flt(d.get("new_exchange_rate"), d.precision("new_exchange_rate")),
"reference_type": "Exchange Rate Revaluation",
"reference_name": self.name,
})
@@ -154,9 +159,9 @@
"party_type": d.get("party_type"),
"party": d.get("party"),
"account_currency": d.get("account_currency"),
- "balance": d.get("balance_in_account_currency"),
- reverse_dr_or_cr: abs(d.get("balance_in_account_currency")),
- "exchange_rate": d.get("current_exchange_rate"),
+ "balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")),
+ reverse_dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")),
+ "exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
"reference_type": "Exchange Rate Revaluation",
"reference_name": self.name
})
@@ -185,9 +190,9 @@
account_details = {}
company_currency = erpnext.get_company_currency(company)
- balance = get_balance_on(account, party_type=party_type, party=party, in_account_currency=False)
+ balance = get_balance_on(account, date=posting_date, party_type=party_type, party=party, in_account_currency=False)
if balance:
- balance_in_account_currency = get_balance_on(account, party_type=party_type, party=party)
+ balance_in_account_currency = get_balance_on(account, date=posting_date, party_type=party_type, party=party)
current_exchange_rate = balance / balance_in_account_currency if balance_in_account_currency else 0
new_exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
new_balance_in_base_currency = balance_in_account_currency * new_exchange_rate
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 11465b7..0844995 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -58,8 +58,8 @@
if not self.get(k):
frappe.throw(_("{0} is required").format(_(self.meta.get_label(k))))
- account_type = frappe.get_cached_value("Account", self.account, "account_type")
if not (self.party_type and self.party):
+ account_type = frappe.get_cached_value("Account", self.account, "account_type")
if account_type == "Receivable":
frappe.throw(_("{0} {1}: Customer is required against Receivable account {2}")
.format(self.voucher_type, self.voucher_no, self.account))
@@ -73,15 +73,19 @@
.format(self.voucher_type, self.voucher_no, self.account))
def pl_must_have_cost_center(self):
- if frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss":
- if not self.cost_center and self.voucher_type != 'Period Closing Voucher':
- msg = _("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}.").format(
- self.voucher_type, self.voucher_no, self.account)
- msg += " "
- msg += _("Please set the cost center field in {0} or setup a default Cost Center for the Company.").format(
- self.voucher_type)
+ """Validate that profit and loss type account GL entries have a cost center."""
- frappe.throw(msg, title=_("Missing Cost Center"))
+ if self.cost_center or self.voucher_type == 'Period Closing Voucher':
+ return
+
+ if frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss":
+ msg = _("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}.").format(
+ self.voucher_type, self.voucher_no, self.account)
+ msg += " "
+ msg += _("Please set the cost center field in {0} or setup a default Cost Center for the Company.").format(
+ self.voucher_type)
+
+ frappe.throw(msg, title=_("Missing Cost Center"))
def validate_dimensions_for_pl_and_bs(self):
account_type = frappe.db.get_value("Account", self.account, "report_type")
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index 6635128..d788d91 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -306,5 +306,5 @@
}
]
})
-
+ jv.flags.ignore_mandatory = True
jv.submit()
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
index 7459c11..33c3e04 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
@@ -1545,6 +1545,7 @@
"fieldname": "consolidated_invoice",
"fieldtype": "Link",
"label": "Consolidated Sales Invoice",
+ "no_copy": 1,
"options": "Sales Invoice",
"read_only": 1
}
@@ -1552,7 +1553,7 @@
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
- "modified": "2021-02-01 15:03:33.800707",
+ "modified": "2021-07-29 13:37:20.636171",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice",
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
index 428989a..0be41b4 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
@@ -558,7 +558,8 @@
"description": "Simple Python Expression, Example: territory != 'All Territories'",
"fieldname": "condition",
"fieldtype": "Code",
- "label": "Condition"
+ "label": "Condition",
+ "options": "PythonExpression"
},
{
"fieldname": "column_break_42",
@@ -575,7 +576,7 @@
"icon": "fa fa-gift",
"idx": 1,
"links": [],
- "modified": "2021-03-06 22:01:24.840422",
+ "modified": "2021-08-06 15:10:04.219321",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Pricing Rule",
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index ffe8be1..3173db1 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -15,6 +15,7 @@
class TestPricingRule(unittest.TestCase):
def setUp(self):
delete_existing_pricing_rules()
+ setup_pricing_rule_data()
def tearDown(self):
delete_existing_pricing_rules()
@@ -554,6 +555,8 @@
for doc in [si, si1]:
doc.delete()
+test_dependencies = ["Campaign"]
+
def make_pricing_rule(**args):
args = frappe._dict(args)
@@ -600,6 +603,13 @@
if args.get(applicable_for):
doc.db_set(applicable_for, args.get(applicable_for))
+def setup_pricing_rule_data():
+ if not frappe.db.exists('Campaign', '_Test Campaign'):
+ frappe.get_doc({
+ 'doctype': 'Campaign',
+ 'campaign_name': '_Test Campaign',
+ 'name': '_Test Campaign'
+ }).insert()
def delete_existing_pricing_rules():
for doctype in ["Pricing Rule", "Pricing Rule Item Code",
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index b54d0e7..94abf3b 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -168,7 +168,7 @@
frappe.throw(_("Invalid {0}").format(args.get(field)))
parent_groups = frappe.db.sql_list("""select name from `tab%s`
- where lft>=%s and rgt<=%s""" % (parenttype, '%s', '%s'), (lft, rgt))
+ where lft<=%s and rgt>=%s""" % (parenttype, '%s', '%s'), (lft, rgt))
if parenttype in ["Customer Group", "Item Group", "Territory"]:
parent_field = "parent_{0}".format(frappe.scrub(parenttype))
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index d6bb69b..79ea017 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -134,7 +134,7 @@
},
get_query_filters: {
docstatus: 1,
- status: ["not in", ["Closed", "Completed"]],
+ status: ["not in", ["Closed", "Completed", "Return Issued"]],
company: me.frm.doc.company,
is_return: 0
}
@@ -275,7 +275,7 @@
// Do not update if inter company reference is there as the details will already be updated
if(this.frm.updating_party_details || this.frm.doc.inter_company_invoice_reference)
return;
-
+
erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details",
{
posting_date: this.frm.doc.posting_date,
@@ -283,7 +283,8 @@
party: this.frm.doc.supplier,
party_type: "Supplier",
account: this.frm.doc.credit_to,
- price_list: this.frm.doc.buying_price_list
+ price_list: this.frm.doc.buying_price_list,
+ fetch_payment_terms_template: cint(!this.frm.doc.ignore_default_payment_terms_template)
}, function() {
me.apply_pricing_rule();
me.frm.doc.apply_tds = me.frm.supplier_tds ? 1 : 0;
@@ -590,4 +591,4 @@
company: function(frm) {
erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
},
-})
+})
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 96ae828..7025dd9 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -132,6 +132,7 @@
"advances",
"payment_schedule_section",
"payment_terms_template",
+ "ignore_default_payment_terms_template",
"payment_schedule",
"terms_section_break",
"tc_name",
@@ -176,7 +177,9 @@
"hidden": 1,
"label": "Title",
"no_copy": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "naming_series",
@@ -188,7 +191,9 @@
"options": "ACC-PINV-.YYYY.-\nACC-PINV-RET-.YYYY.-",
"print_hide": 1,
"reqd": 1,
- "set_only_once": 1
+ "set_only_once": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "supplier",
@@ -200,7 +205,9 @@
"options": "Supplier",
"print_hide": 1,
"reqd": 1,
- "search_index": 1
+ "search_index": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"bold": 1,
@@ -212,7 +219,9 @@
"label": "Supplier Name",
"oldfieldname": "supplier_name",
"oldfieldtype": "Data",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fetch_from": "supplier.tax_id",
@@ -220,21 +229,27 @@
"fieldtype": "Read Only",
"label": "Tax Id",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "due_date",
"fieldtype": "Date",
"label": "Due Date",
"oldfieldname": "due_date",
- "oldfieldtype": "Date"
+ "oldfieldtype": "Date",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
"fieldname": "is_paid",
"fieldtype": "Check",
"label": "Is Paid",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -242,19 +257,25 @@
"fieldtype": "Check",
"label": "Is Return (Debit Note)",
"no_copy": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
"fieldname": "apply_tds",
"fieldtype": "Check",
"label": "Apply Tax Withholding Amount",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break1",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1,
"width": "50%"
},
{
@@ -264,13 +285,17 @@
"label": "Company",
"options": "Company",
"print_hide": 1,
- "remember_last_selected_value": 1
+ "remember_last_selected_value": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
- "options": "Cost Center"
+ "options": "Cost Center",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "Today",
@@ -282,7 +307,9 @@
"oldfieldtype": "Date",
"print_hide": 1,
"reqd": 1,
- "search_index": 1
+ "search_index": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "posting_time",
@@ -291,6 +318,8 @@
"no_copy": 1,
"print_hide": 1,
"print_width": "100px",
+ "show_days": 1,
+ "show_seconds": 1,
"width": "100px"
},
{
@@ -299,7 +328,9 @@
"fieldname": "set_posting_time",
"fieldtype": "Check",
"label": "Edit Posting Date and Time",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "amended_from",
@@ -311,44 +342,58 @@
"oldfieldtype": "Link",
"options": "Purchase Invoice",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
"collapsible_depends_on": "eval:doc.on_hold",
"fieldname": "sb_14",
"fieldtype": "Section Break",
- "label": "Hold Invoice"
+ "label": "Hold Invoice",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
"fieldname": "on_hold",
"fieldtype": "Check",
- "label": "Hold Invoice"
+ "label": "Hold Invoice",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:doc.on_hold",
"description": "Once set, this invoice will be on hold till the set date",
"fieldname": "release_date",
"fieldtype": "Date",
- "label": "Release Date"
+ "label": "Release Date",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "cb_17",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:doc.on_hold",
"fieldname": "hold_comment",
"fieldtype": "Small Text",
- "label": "Reason For Putting On Hold"
+ "label": "Reason For Putting On Hold",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
"collapsible_depends_on": "bill_no",
"fieldname": "supplier_invoice_details",
"fieldtype": "Section Break",
- "label": "Supplier Invoice Details"
+ "label": "Supplier Invoice Details",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "bill_no",
@@ -356,11 +401,15 @@
"label": "Supplier Invoice No",
"oldfieldname": "bill_no",
"oldfieldtype": "Data",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_15",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "bill_date",
@@ -369,13 +418,17 @@
"no_copy": 1,
"oldfieldname": "bill_date",
"oldfieldtype": "Date",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "return_against",
"fieldname": "returns",
"fieldtype": "Section Break",
- "label": "Returns"
+ "label": "Returns",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "return_against",
@@ -385,26 +438,34 @@
"no_copy": 1,
"options": "Purchase Invoice",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
"fieldname": "section_addresses",
"fieldtype": "Section Break",
- "label": "Address and Contact"
+ "label": "Address and Contact",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "supplier_address",
"fieldtype": "Link",
"label": "Select Supplier Address",
"options": "Address",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "address_display",
"fieldtype": "Small Text",
"label": "Address",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "contact_person",
@@ -412,51 +473,67 @@
"in_global_search": 1,
"label": "Contact Person",
"options": "Contact",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "contact_display",
"fieldtype": "Small Text",
"label": "Contact",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "contact_mobile",
"fieldtype": "Small Text",
"label": "Mobile No",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "contact_email",
"fieldtype": "Small Text",
"label": "Contact Email",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "col_break_address",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "shipping_address",
"fieldtype": "Link",
"label": "Select Shipping Address",
"options": "Address",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "shipping_address_display",
"fieldtype": "Small Text",
"label": "Shipping Address",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
"fieldname": "currency_and_price_list",
"fieldtype": "Section Break",
"label": "Currency and Price List",
- "options": "fa fa-tag"
+ "options": "fa fa-tag",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "currency",
@@ -465,7 +542,9 @@
"oldfieldname": "currency",
"oldfieldtype": "Select",
"options": "Currency",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "conversion_rate",
@@ -474,18 +553,24 @@
"oldfieldname": "conversion_rate",
"oldfieldtype": "Currency",
"precision": "9",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break2",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "buying_price_list",
"fieldtype": "Link",
"label": "Price List",
"options": "Price List",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "price_list_currency",
@@ -493,14 +578,18 @@
"label": "Price List Currency",
"options": "Currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "plc_conversion_rate",
"fieldtype": "Float",
"label": "Price List Exchange Rate",
"precision": "9",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -509,11 +598,15 @@
"label": "Ignore Pricing Rule",
"no_copy": 1,
"permlevel": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "sec_warehouse",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "update_stock",
@@ -522,7 +615,9 @@
"fieldtype": "Link",
"label": "Set Accepted Warehouse",
"options": "Warehouse",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "update_stock",
@@ -532,11 +627,15 @@
"label": "Rejected Warehouse",
"no_copy": 1,
"options": "Warehouse",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "col_break_warehouse",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "No",
@@ -544,25 +643,33 @@
"fieldtype": "Select",
"label": "Raw Materials Supplied",
"options": "No\nYes",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "items_section",
"fieldtype": "Section Break",
"oldfieldtype": "Section Break",
- "options": "fa fa-shopping-cart"
+ "options": "fa fa-shopping-cart",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
"fieldname": "update_stock",
"fieldtype": "Check",
"label": "Update Stock",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "scan_barcode",
"fieldtype": "Data",
- "label": "Scan Barcode"
+ "label": "Scan Barcode",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_bulk_edit": 1,
@@ -572,25 +679,33 @@
"oldfieldname": "entries",
"oldfieldtype": "Table",
"options": "Purchase Invoice Item",
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "pricing_rule_details",
"fieldtype": "Section Break",
- "label": "Pricing Rules"
+ "label": "Pricing Rules",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "pricing_rules",
"fieldtype": "Table",
"label": "Pricing Rule Detail",
"options": "Pricing Rule Detail",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible_depends_on": "supplied_items",
"fieldname": "raw_materials_supplied",
"fieldtype": "Section Break",
- "label": "Raw Materials Supplied"
+ "label": "Raw Materials Supplied",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "update_stock",
@@ -598,17 +713,23 @@
"fieldtype": "Table",
"label": "Supplied Items",
"no_copy": 1,
- "options": "Purchase Receipt Item Supplied"
+ "options": "Purchase Receipt Item Supplied",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "section_break_26",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "total_qty",
"fieldtype": "Float",
"label": "Total Quantity",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_total",
@@ -616,7 +737,9 @@
"label": "Total (Company Currency)",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_net_total",
@@ -626,18 +749,24 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_28",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "total",
"fieldtype": "Currency",
"label": "Total",
"options": "currency",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "net_total",
@@ -647,42 +776,56 @@
"oldfieldtype": "Currency",
"options": "currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "total_net_weight",
"fieldtype": "Float",
"label": "Total Net Weight",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "taxes_section",
"fieldtype": "Section Break",
"oldfieldtype": "Section Break",
- "options": "fa fa-money"
+ "options": "fa fa-money",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "tax_category",
"fieldtype": "Link",
"label": "Tax Category",
"options": "Tax Category",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_49",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "shipping_rule",
"fieldtype": "Link",
"label": "Shipping Rule",
"options": "Shipping Rule",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "section_break_51",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "taxes_and_charges",
@@ -691,7 +834,9 @@
"oldfieldname": "purchase_other_charges",
"oldfieldtype": "Link",
"options": "Purchase Taxes and Charges Template",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "taxes",
@@ -699,13 +844,17 @@
"label": "Purchase Taxes and Charges",
"oldfieldname": "purchase_tax_details",
"oldfieldtype": "Table",
- "options": "Purchase Taxes and Charges"
+ "options": "Purchase Taxes and Charges",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
"fieldname": "sec_tax_breakup",
"fieldtype": "Section Break",
- "label": "Tax Breakup"
+ "label": "Tax Breakup",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "other_charges_calculation",
@@ -714,13 +863,17 @@
"no_copy": 1,
"oldfieldtype": "HTML",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "totals",
"fieldtype": "Section Break",
"oldfieldtype": "Section Break",
- "options": "fa fa-money"
+ "options": "fa fa-money",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_taxes_and_charges_added",
@@ -730,7 +883,9 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_taxes_and_charges_deducted",
@@ -740,7 +895,9 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_total_taxes_and_charges",
@@ -750,11 +907,15 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_40",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "taxes_and_charges_added",
@@ -764,7 +925,9 @@
"oldfieldtype": "Currency",
"options": "currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "taxes_and_charges_deducted",
@@ -774,7 +937,9 @@
"oldfieldtype": "Currency",
"options": "currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "total_taxes_and_charges",
@@ -782,14 +947,18 @@
"label": "Total Taxes and Charges",
"options": "currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
"collapsible_depends_on": "discount_amount",
"fieldname": "section_break_44",
"fieldtype": "Section Break",
- "label": "Additional Discount"
+ "label": "Additional Discount",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "Grand Total",
@@ -797,7 +966,9 @@
"fieldtype": "Select",
"label": "Apply Additional Discount On",
"options": "\nGrand Total\nNet Total",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_discount_amount",
@@ -805,28 +976,38 @@
"label": "Additional Discount Amount (Company Currency)",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_46",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "additional_discount_percentage",
"fieldtype": "Float",
"label": "Additional Discount Percentage",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "discount_amount",
"fieldtype": "Currency",
"label": "Additional Discount Amount",
"options": "currency",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "section_break_49",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_grand_total",
@@ -836,7 +1017,9 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -846,7 +1029,9 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -856,7 +1041,9 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_in_words",
@@ -866,13 +1053,17 @@
"oldfieldname": "in_words",
"oldfieldtype": "Data",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break8",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
"print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1,
"width": "50%"
},
{
@@ -883,7 +1074,9 @@
"oldfieldname": "grand_total_import",
"oldfieldtype": "Currency",
"options": "currency",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -893,7 +1086,9 @@
"no_copy": 1,
"options": "currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -903,7 +1098,9 @@
"no_copy": 1,
"options": "currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "in_words",
@@ -913,7 +1110,9 @@
"oldfieldname": "in_words_import",
"oldfieldtype": "Data",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "total_advance",
@@ -924,7 +1123,9 @@
"oldfieldtype": "Currency",
"options": "party_account_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "outstanding_amount",
@@ -935,14 +1136,18 @@
"oldfieldtype": "Currency",
"options": "party_account_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
"depends_on": "grand_total",
"fieldname": "disable_rounded_total",
"fieldtype": "Check",
- "label": "Disable Rounded Total"
+ "label": "Disable Rounded Total",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -950,20 +1155,26 @@
"depends_on": "eval:doc.is_paid===1||(doc.advances && doc.advances.length>0)",
"fieldname": "payments_section",
"fieldtype": "Section Break",
- "label": "Payments"
+ "label": "Payments",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "mode_of_payment",
"fieldtype": "Link",
"label": "Mode of Payment",
"options": "Mode of Payment",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "cash_bank_account",
"fieldtype": "Link",
"label": "Cash/Bank Account",
- "options": "Account"
+ "options": "Account",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "clearance_date",
@@ -971,11 +1182,15 @@
"label": "Clearance Date",
"no_copy": 1,
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "col_br_payments",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "is_paid",
@@ -984,7 +1199,9 @@
"label": "Paid Amount",
"no_copy": 1,
"options": "currency",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_paid_amount",
@@ -993,7 +1210,9 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1001,7 +1220,9 @@
"depends_on": "grand_total",
"fieldname": "write_off",
"fieldtype": "Section Break",
- "label": "Write Off"
+ "label": "Write Off",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "write_off_amount",
@@ -1009,7 +1230,9 @@
"label": "Write Off Amount",
"no_copy": 1,
"options": "currency",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_write_off_amount",
@@ -1018,11 +1241,15 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_61",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:flt(doc.write_off_amount)!=0",
@@ -1030,7 +1257,9 @@
"fieldtype": "Link",
"label": "Write Off Account",
"options": "Account",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:flt(doc.write_off_amount)!=0",
@@ -1038,7 +1267,9 @@
"fieldtype": "Link",
"label": "Write Off Cost Center",
"options": "Cost Center",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1048,13 +1279,17 @@
"label": "Advance Payments",
"oldfieldtype": "Section Break",
"options": "fa fa-money",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
"fieldname": "allocate_advances_automatically",
"fieldtype": "Check",
- "label": "Set Advances and Allocate (FIFO)"
+ "label": "Set Advances and Allocate (FIFO)",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:!doc.allocate_advances_automatically",
@@ -1062,7 +1297,9 @@
"fieldtype": "Button",
"label": "Get Advances Paid",
"oldfieldtype": "Button",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "advances",
@@ -1072,20 +1309,26 @@
"oldfieldname": "advance_allocation_details",
"oldfieldtype": "Table",
"options": "Purchase Invoice Advance",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
"collapsible_depends_on": "eval:(!doc.is_return)",
"fieldname": "payment_schedule_section",
"fieldtype": "Section Break",
- "label": "Payment Terms"
+ "label": "Payment Terms",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "payment_terms_template",
"fieldtype": "Link",
"label": "Payment Terms Template",
- "options": "Payment Terms Template"
+ "options": "Payment Terms Template",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "payment_schedule",
@@ -1093,7 +1336,9 @@
"label": "Payment Schedule",
"no_copy": 1,
"options": "Payment Schedule",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1101,25 +1346,33 @@
"fieldname": "terms_section_break",
"fieldtype": "Section Break",
"label": "Terms and Conditions",
- "options": "fa fa-legal"
+ "options": "fa fa-legal",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "tc_name",
"fieldtype": "Link",
"label": "Terms",
"options": "Terms and Conditions",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "terms",
"fieldtype": "Text Editor",
- "label": "Terms and Conditions1"
+ "label": "Terms and Conditions1",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
"fieldname": "printing_settings",
"fieldtype": "Section Break",
- "label": "Printing Settings"
+ "label": "Printing Settings",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -1127,7 +1380,9 @@
"fieldtype": "Link",
"label": "Letter Head",
"options": "Letter Head",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -1135,11 +1390,15 @@
"fieldname": "group_same_items",
"fieldtype": "Check",
"label": "Group same items",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_112",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -1151,14 +1410,18 @@
"oldfieldtype": "Link",
"options": "Print Heading",
"print_hide": 1,
- "report_hide": 1
+ "report_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "language",
"fieldtype": "Data",
"label": "Print Language",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1167,7 +1430,9 @@
"label": "More Information",
"oldfieldtype": "Section Break",
"options": "fa fa-file-text",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "credit_to",
@@ -1178,7 +1443,9 @@
"options": "Account",
"print_hide": 1,
"reqd": 1,
- "search_index": 1
+ "search_index": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "party_account_currency",
@@ -1188,7 +1455,9 @@
"no_copy": 1,
"options": "Currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "No",
@@ -1198,7 +1467,9 @@
"oldfieldname": "is_opening",
"oldfieldtype": "Select",
"options": "No\nYes",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "against_expense_account",
@@ -1208,11 +1479,15 @@
"no_copy": 1,
"oldfieldname": "against_expense_account",
"oldfieldtype": "Small Text",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_63",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "Draft",
@@ -1221,7 +1496,9 @@
"in_standard_filter": 1,
"label": "Status",
"options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled\nInternal Transfer",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "inter_company_invoice_reference",
@@ -1230,7 +1507,9 @@
"no_copy": 1,
"options": "Sales Invoice",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "remarks",
@@ -1239,14 +1518,18 @@
"no_copy": 1,
"oldfieldname": "remarks",
"oldfieldtype": "Text",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
"fieldname": "subscription_section",
"fieldtype": "Section Break",
"label": "Subscription Section",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -1255,7 +1538,9 @@
"fieldtype": "Date",
"label": "From Date",
"no_copy": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -1264,11 +1549,15 @@
"fieldtype": "Date",
"label": "To Date",
"no_copy": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_114",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "auto_repeat",
@@ -1277,24 +1566,32 @@
"no_copy": 1,
"options": "Auto Repeat",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
"depends_on": "eval: doc.auto_repeat",
"fieldname": "update_auto_repeat_reference",
"fieldtype": "Button",
- "label": "Update Auto Repeat Reference"
+ "label": "Update Auto Repeat Reference",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
- "label": "Accounting Dimensions "
+ "label": "Accounting Dimensions ",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "dimension_col_break",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -1302,7 +1599,9 @@
"fieldname": "is_internal_supplier",
"fieldtype": "Check",
"label": "Is Internal Supplier",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "tax_withholding_category",
@@ -1310,25 +1609,33 @@
"hidden": 1,
"label": "Tax Withholding Category",
"options": "Tax Withholding Category",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "billing_address",
"fieldtype": "Link",
"label": "Select Billing Address",
- "options": "Address"
+ "options": "Address",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "billing_address_display",
"fieldtype": "Small Text",
"label": "Billing Address",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
- "options": "Project"
+ "options": "Project",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:doc.is_internal_supplier",
@@ -1336,7 +1643,9 @@
"fieldname": "unrealized_profit_loss_account",
"fieldtype": "Link",
"label": "Unrealized Profit / Loss Account",
- "options": "Account"
+ "options": "Account",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:doc.is_internal_supplier",
@@ -1345,7 +1654,9 @@
"fieldname": "represents_company",
"fieldtype": "Link",
"label": "Represents Company",
- "options": "Company"
+ "options": "Company",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:doc.update_stock && doc.is_internal_supplier",
@@ -1357,6 +1668,8 @@
"options": "Warehouse",
"print_hide": 1,
"print_width": "50px",
+ "show_days": 1,
+ "show_seconds": 1,
"width": "50px"
},
{
@@ -1368,6 +1681,8 @@
"options": "Warehouse",
"print_hide": 1,
"print_width": "50px",
+ "show_days": 1,
+ "show_seconds": 1,
"width": "50px"
},
{
@@ -1384,13 +1699,23 @@
"fieldtype": "Link",
"label": "Additional Discount Account",
"options": "Account"
+ },
+ {
+ "default": "0",
+ "fieldname": "ignore_default_payment_terms_template",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Ignore Default Payment Terms Template",
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
}
],
"icon": "fa fa-file-text",
"idx": 204,
"is_submittable": 1,
"links": [],
- "modified": "2021-07-17 17:37:50.570595",
+ "modified": "2021-08-07 17:53:14.351439",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 76ef23e..a95f971 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -22,11 +22,13 @@
from frappe.model.mapper import get_mapped_doc
from six import iteritems
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\
- unlink_inter_company_doc
+ unlink_inter_company_doc, check_if_return_invoice_linked_with_payment_entry
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
from erpnext.accounts.deferred_revenue import validate_service_stop_date
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import get_item_account_wise_additional_cost
+class WarehouseMissingError(frappe.ValidationError): pass
+
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
}
@@ -207,8 +209,8 @@
if self.update_stock and for_validate:
for d in self.get('items'):
if not d.warehouse:
- frappe.throw(_("Warehouse required at Row No {0}, please set default warehouse for the item {1} for the company {2}").
- format(d.idx, d.item_code, self.company))
+ frappe.throw(_("Row No {0}: Warehouse is required. Please set a Default Warehouse for Item {1} and Company {2}").
+ format(d.idx, d.item_code, self.company), exc=WarehouseMissingError)
super(PurchaseInvoice, self).validate_warehouse()
@@ -246,7 +248,7 @@
and (not item.po_detail or
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 item.warehouse and (not item.from_warehouse):
if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]:
msg = _("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))
@@ -660,7 +662,7 @@
)
gl_entries.append(
self.get_gl_dict({
- "account": self.get_company_default("exchange_gain_loss_account"),
+ "account": self.get_company_default("exchange_gain_loss_account"),
"against": self.supplier,
"credit": discrepancy_caused_by_exchange_rate_difference,
"cost_center": item.cost_center,
@@ -1018,6 +1020,8 @@
}, item=self))
def on_cancel(self):
+ check_if_return_invoice_linked_with_payment_entry(self)
+
super(PurchaseInvoice, self).on_cancel()
self.check_on_hold_or_closed_status()
@@ -1199,7 +1203,7 @@
purchase_receipts_or_invoices.append(item.get(doc_reference))
if item.get(items_reference):
items.append(item.get(items_reference))
-
+
exchange_rate_map = frappe._dict(frappe.get_all(parent_doctype, filters={'name': ('in',
purchase_receipts_or_invoices)}, fields=['name', 'conversion_rate'], as_list=1))
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index c211e50..8e6393f 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -240,7 +240,7 @@
pi.conversion_rate = 80
pi.insert()
- pi.submit()
+ pi.submit()
# Get exchnage gain and loss account
exchange_gain_loss_account = frappe.db.get_value('Company', pi.company, 'exchange_gain_loss_account')
@@ -1022,7 +1022,7 @@
unlink_enabled = frappe.db.get_value(
"Accounts Settings", "Accounts Settings",
"unlink_payment_on_cancel_of_invoice")
-
+
frappe.db.set_value(
"Accounts Settings", "Accounts Settings",
"unlink_payment_on_cancel_of_invoice", 1)
@@ -1062,8 +1062,8 @@
expected_gle = [
["_Test Account Cost for Goods Sold - _TC", 37500.0],
- ["_Test Payable USD - _TC", -40000.0],
- ["Exchange Gain/Loss - _TC", 2500.0]
+ ["_Test Payable USD - _TC", -35000.0],
+ ["Exchange Gain/Loss - _TC", -2500.0]
]
gl_entries = frappe.db.sql("""
@@ -1071,7 +1071,7 @@
where voucher_no=%s
group by account
order by account asc""", (pi.name), as_dict=1)
-
+
for i, gle in enumerate(gl_entries):
self.assertEqual(expected_gle[i][0], gle.account)
self.assertEqual(expected_gle[i][1], gle.balance)
@@ -1093,8 +1093,8 @@
expected_gle = [
["_Test Account Cost for Goods Sold - _TC", 36500.0],
- ["_Test Payable USD - _TC", -38000.0],
- ["Exchange Gain/Loss - _TC", 1500.0]
+ ["_Test Payable USD - _TC", -35000.0],
+ ["Exchange Gain/Loss - _TC", -1500.0]
]
gl_entries = frappe.db.sql("""
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index 922b567..51e5c1a 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -502,6 +502,7 @@
},
{
"collapsible": 1,
+ "collapsible_depends_on": "enable_deferred_expense",
"fieldname": "deferred_expense_section",
"fieldtype": "Section Break",
"label": "Deferred Expense"
@@ -861,7 +862,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2021-07-13 02:04:37.787882",
+ "modified": "2021-08-12 20:14:45.506639",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json
index 1fa68e0..d86abad 100644
--- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json
+++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json
@@ -22,7 +22,7 @@
"cost_center",
"dimension_col_break",
"section_break_9",
- "currency",
+ "account_currency",
"tax_amount",
"tax_amount_after_discount_amount",
"total",
@@ -209,26 +209,26 @@
"fieldtype": "Column Break"
},
{
- "fetch_from": "account_head.account_currency",
- "fieldname": "currency",
- "fieldtype": "Link",
- "label": "Account Currency",
- "options": "Currency",
- "read_only": 1
- },
- {
"default": "0",
"depends_on": "eval:['Purchase Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)",
"description": "If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry",
"fieldname": "included_in_paid_amount",
"fieldtype": "Check",
"label": "Considered In Paid Amount"
+ },
+ {
+ "fetch_from": "account_head.account_currency",
+ "fieldname": "account_currency",
+ "fieldtype": "Link",
+ "label": "Account Currency",
+ "options": "Currency",
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2021-06-14 01:43:50.750455",
+ "modified": "2021-08-05 20:04:36.618240",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Taxes and Charges",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 5c09b71..9ab3547 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -48,6 +48,8 @@
"shipping_address",
"company_address",
"company_address_display",
+ "dispatch_address_name",
+ "dispatch_address",
"currency_and_price_list",
"currency",
"conversion_rate",
@@ -126,6 +128,7 @@
"get_advances",
"advances",
"payment_schedule_section",
+ "ignore_default_payment_terms_template",
"payment_terms_template",
"payment_schedule",
"payments_section",
@@ -196,7 +199,9 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "options": "fa fa-user"
+ "options": "fa fa-user",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -208,7 +213,9 @@
"hide_seconds": 1,
"label": "Title",
"no_copy": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"bold": 1,
@@ -223,7 +230,9 @@
"options": "ACC-SINV-.YYYY.-\nACC-SINV-RET-.YYYY.-",
"print_hide": 1,
"reqd": 1,
- "set_only_once": 1
+ "set_only_once": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"bold": 1,
@@ -237,7 +246,9 @@
"oldfieldtype": "Link",
"options": "Customer",
"print_hide": 1,
- "search_index": 1
+ "search_index": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"bold": 1,
@@ -251,7 +262,9 @@
"label": "Customer Name",
"oldfieldname": "customer_name",
"oldfieldtype": "Data",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "tax_id",
@@ -260,7 +273,9 @@
"hide_seconds": 1,
"label": "Tax Id",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "project",
@@ -272,7 +287,9 @@
"oldfieldname": "project_name",
"oldfieldtype": "Link",
"options": "Project",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -283,7 +300,9 @@
"label": "Include Payment (POS)",
"oldfieldname": "is_pos",
"oldfieldtype": "Check",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "is_pos",
@@ -293,7 +312,9 @@
"hide_seconds": 1,
"label": "POS Profile",
"options": "POS Profile",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -303,14 +324,18 @@
"hide_seconds": 1,
"label": "Is Return (Credit Note)",
"no_copy": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break1",
"fieldtype": "Column Break",
"hide_days": 1,
"hide_seconds": 1,
- "oldfieldtype": "Column Break"
+ "oldfieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "company",
@@ -324,7 +349,9 @@
"options": "Company",
"print_hide": 1,
"remember_last_selected_value": 1,
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "cost_center",
@@ -332,7 +359,9 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Cost Center",
- "options": "Cost Center"
+ "options": "Cost Center",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"bold": 1,
@@ -346,7 +375,9 @@
"oldfieldname": "posting_date",
"oldfieldtype": "Date",
"reqd": 1,
- "search_index": 1
+ "search_index": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "posting_time",
@@ -357,7 +388,9 @@
"no_copy": 1,
"oldfieldname": "posting_time",
"oldfieldtype": "Time",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -367,7 +400,9 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Edit Posting Date and Time",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "due_date",
@@ -377,7 +412,9 @@
"label": "Payment Due Date",
"no_copy": 1,
"oldfieldname": "due_date",
- "oldfieldtype": "Date"
+ "oldfieldtype": "Date",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "amended_from",
@@ -391,7 +428,9 @@
"oldfieldtype": "Link",
"options": "Sales Invoice",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:doc.return_against || doc.is_debit_note",
@@ -404,7 +443,9 @@
"options": "Sales Invoice",
"print_hide": 1,
"read_only_depends_on": "eval:doc.is_return",
- "search_index": 1
+ "search_index": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -413,7 +454,9 @@
"fieldtype": "Check",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Update Billed Amount in Sales Order"
+ "label": "Update Billed Amount in Sales Order",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -422,7 +465,9 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Customer PO Details"
+ "label": "Customer PO Details",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -432,13 +477,17 @@
"hide_seconds": 1,
"label": "Customer's Purchase Order",
"no_copy": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_23",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -446,7 +495,9 @@
"fieldtype": "Date",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Customer's Purchase Order Date"
+ "label": "Customer's Purchase Order Date",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -454,7 +505,9 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Address and Contact"
+ "label": "Address and Contact",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "customer_address",
@@ -463,7 +516,9 @@
"hide_seconds": 1,
"label": "Customer Address",
"options": "Address",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "address_display",
@@ -471,7 +526,9 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Address",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "contact_person",
@@ -481,7 +538,9 @@
"in_global_search": 1,
"label": "Contact Person",
"options": "Contact",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "contact_display",
@@ -489,7 +548,9 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Contact",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "contact_mobile",
@@ -498,7 +559,9 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Mobile No",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "contact_email",
@@ -509,7 +572,9 @@
"label": "Contact Email",
"options": "Email",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "territory",
@@ -518,13 +583,17 @@
"hide_seconds": 1,
"label": "Territory",
"options": "Territory",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "col_break4",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "shipping_address_name",
@@ -533,7 +602,9 @@
"hide_seconds": 1,
"label": "Shipping Address Name",
"options": "Address",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "shipping_address",
@@ -542,7 +613,9 @@
"hide_seconds": 1,
"label": "Shipping Address",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "company_address",
@@ -551,7 +624,9 @@
"hide_seconds": 1,
"label": "Company Address Name",
"options": "Address",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "company_address_display",
@@ -561,7 +636,9 @@
"hide_seconds": 1,
"label": "Company Address",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -570,7 +647,9 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Currency and Price List"
+ "label": "Currency and Price List",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "currency",
@@ -582,7 +661,9 @@
"oldfieldtype": "Select",
"options": "Currency",
"print_hide": 1,
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"description": "Rate at which Customer Currency is converted to customer's base currency",
@@ -595,13 +676,17 @@
"oldfieldtype": "Currency",
"precision": "9",
"print_hide": 1,
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break2",
"fieldtype": "Column Break",
"hide_days": 1,
"hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1,
"width": "50%"
},
{
@@ -614,7 +699,9 @@
"oldfieldtype": "Select",
"options": "Price List",
"print_hide": 1,
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "price_list_currency",
@@ -625,7 +712,9 @@
"options": "Currency",
"print_hide": 1,
"read_only": 1,
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"description": "Rate at which Price list currency is converted to customer's base currency",
@@ -636,7 +725,9 @@
"label": "Price List Exchange Rate",
"precision": "9",
"print_hide": 1,
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -647,14 +738,18 @@
"label": "Ignore Pricing Rule",
"no_copy": 1,
"permlevel": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "sec_warehouse",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Warehouse"
+ "label": "Warehouse",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "update_stock",
@@ -664,7 +759,9 @@
"hide_seconds": 1,
"label": "Source Warehouse",
"options": "Warehouse",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "items_section",
@@ -673,7 +770,9 @@
"hide_seconds": 1,
"label": "Items",
"oldfieldtype": "Section Break",
- "options": "fa fa-shopping-cart"
+ "options": "fa fa-shopping-cart",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -684,14 +783,18 @@
"label": "Update Stock",
"oldfieldname": "update_stock",
"oldfieldtype": "Check",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "scan_barcode",
"fieldtype": "Data",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Scan Barcode"
+ "label": "Scan Barcode",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_bulk_edit": 1,
@@ -703,14 +806,18 @@
"oldfieldname": "entries",
"oldfieldtype": "Table",
"options": "Sales Invoice Item",
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "pricing_rule_details",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Pricing Rules"
+ "label": "Pricing Rules",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "pricing_rules",
@@ -719,7 +826,9 @@
"hide_seconds": 1,
"label": "Pricing Rule Detail",
"options": "Pricing Rule Detail",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "packing_list",
@@ -728,7 +837,9 @@
"hide_seconds": 1,
"label": "Packing List",
"options": "fa fa-suitcase",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "packed_items",
@@ -737,7 +848,9 @@
"hide_seconds": 1,
"label": "Packed Items",
"options": "Packed Item",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "product_bundle_help",
@@ -745,7 +858,9 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Product Bundle Help",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -755,7 +870,9 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Time Sheet List"
+ "label": "Time Sheet List",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "timesheets",
@@ -764,7 +881,9 @@
"hide_seconds": 1,
"label": "Time Sheets",
"options": "Sales Invoice Timesheet",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -775,13 +894,17 @@
"label": "Total Billing Amount",
"options": "currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "section_break_30",
"fieldtype": "Section Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "total_qty",
@@ -789,7 +912,9 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Total Quantity",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_total",
@@ -799,7 +924,9 @@
"label": "Total (Company Currency)",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_net_total",
@@ -812,13 +939,17 @@
"options": "Company:company:default_currency",
"print_hide": 1,
"read_only": 1,
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_32",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "total",
@@ -827,7 +958,9 @@
"hide_seconds": 1,
"label": "Total",
"options": "currency",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "net_total",
@@ -837,7 +970,9 @@
"label": "Net Total",
"options": "currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "total_net_weight",
@@ -846,7 +981,9 @@
"hide_seconds": 1,
"label": "Total Net Weight",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "taxes_section",
@@ -854,7 +991,9 @@
"hide_days": 1,
"hide_seconds": 1,
"oldfieldtype": "Section Break",
- "options": "fa fa-money"
+ "options": "fa fa-money",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "taxes_and_charges",
@@ -865,13 +1004,17 @@
"oldfieldname": "charge",
"oldfieldtype": "Link",
"options": "Sales Taxes and Charges Template",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_38",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "shipping_rule",
@@ -881,7 +1024,9 @@
"label": "Shipping Rule",
"oldfieldtype": "Button",
"options": "Shipping Rule",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "tax_category",
@@ -890,13 +1035,17 @@
"hide_seconds": 1,
"label": "Tax Category",
"options": "Tax Category",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "section_break_40",
"fieldtype": "Section Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "taxes",
@@ -906,7 +1055,9 @@
"label": "Sales Taxes and Charges",
"oldfieldname": "other_charges",
"oldfieldtype": "Table",
- "options": "Sales Taxes and Charges"
+ "options": "Sales Taxes and Charges",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -914,7 +1065,9 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Tax Breakup"
+ "label": "Tax Breakup",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "other_charges_calculation",
@@ -925,13 +1078,17 @@
"no_copy": 1,
"oldfieldtype": "HTML",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "section_break_43",
"fieldtype": "Section Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_total_taxes_and_charges",
@@ -943,13 +1100,17 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_47",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "total_taxes_and_charges",
@@ -959,7 +1120,9 @@
"label": "Total Taxes and Charges",
"options": "currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -967,7 +1130,9 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Loyalty Points Redemption"
+ "label": "Loyalty Points Redemption",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "redeem_loyalty_points",
@@ -977,7 +1142,9 @@
"hide_seconds": 1,
"label": "Loyalty Points",
"no_copy": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "redeem_loyalty_points",
@@ -989,7 +1156,9 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -999,13 +1168,17 @@
"hide_seconds": 1,
"label": "Redeem Loyalty Points",
"no_copy": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_77",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fetch_from": "customer.loyalty_program",
@@ -1017,7 +1190,9 @@
"no_copy": 1,
"options": "Loyalty Program",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "redeem_loyalty_points",
@@ -1027,7 +1202,9 @@
"hide_seconds": 1,
"label": "Redemption Account",
"no_copy": 1,
- "options": "Account"
+ "options": "Account",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "redeem_loyalty_points",
@@ -1037,7 +1214,9 @@
"hide_seconds": 1,
"label": "Redemption Cost Center",
"no_copy": 1,
- "options": "Cost Center"
+ "options": "Cost Center",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1046,7 +1225,9 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Additional Discount"
+ "label": "Additional Discount",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "Grand Total",
@@ -1056,7 +1237,9 @@
"hide_seconds": 1,
"label": "Apply Additional Discount On",
"options": "\nGrand Total\nNet Total",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_discount_amount",
@@ -1066,13 +1249,17 @@
"label": "Additional Discount Amount (Company Currency)",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_51",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "additional_discount_percentage",
@@ -1080,7 +1267,9 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Additional Discount Percentage",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "discount_amount",
@@ -1089,7 +1278,9 @@
"hide_seconds": 1,
"label": "Additional Discount Amount",
"options": "currency",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "totals",
@@ -1098,7 +1289,9 @@
"hide_seconds": 1,
"oldfieldtype": "Section Break",
"options": "fa fa-money",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_grand_total",
@@ -1111,7 +1304,9 @@
"options": "Company:company:default_currency",
"print_hide": 1,
"read_only": 1,
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -1123,7 +1318,9 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -1136,7 +1333,9 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"description": "In Words will be visible once you save the Sales Invoice.",
@@ -1149,7 +1348,9 @@
"oldfieldname": "in_words",
"oldfieldtype": "Data",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break5",
@@ -1158,6 +1359,8 @@
"hide_seconds": 1,
"oldfieldtype": "Column Break",
"print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1,
"width": "50%"
},
{
@@ -1172,7 +1375,9 @@
"oldfieldtype": "Currency",
"options": "currency",
"read_only": 1,
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -1184,7 +1389,9 @@
"no_copy": 1,
"options": "currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"bold": 1,
@@ -1197,7 +1404,9 @@
"oldfieldname": "rounded_total_export",
"oldfieldtype": "Currency",
"options": "currency",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "in_words",
@@ -1209,7 +1418,9 @@
"oldfieldname": "in_words_export",
"oldfieldtype": "Data",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "total_advance",
@@ -1221,7 +1432,9 @@
"oldfieldtype": "Currency",
"options": "party_account_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "outstanding_amount",
@@ -1234,7 +1447,9 @@
"oldfieldtype": "Currency",
"options": "party_account_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1246,7 +1461,9 @@
"label": "Advance Payments",
"oldfieldtype": "Section Break",
"options": "fa fa-money",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -1254,7 +1471,9 @@
"fieldtype": "Check",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Allocate Advances Automatically (FIFO)"
+ "label": "Allocate Advances Automatically (FIFO)",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:!doc.allocate_advances_automatically",
@@ -1263,7 +1482,9 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Get Advances Received",
- "options": "set_advances"
+ "options": "set_advances",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "advances",
@@ -1274,7 +1495,9 @@
"oldfieldname": "advance_adjustment_details",
"oldfieldtype": "Table",
"options": "Sales Invoice Advance",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1283,7 +1506,9 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Payment Terms"
+ "label": "Payment Terms",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:(!doc.is_pos && !doc.is_return)",
@@ -1294,7 +1519,9 @@
"label": "Payment Terms Template",
"no_copy": 1,
"options": "Payment Terms Template",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:(!doc.is_pos && !doc.is_return)",
@@ -1305,7 +1532,9 @@
"label": "Payment Schedule",
"no_copy": 1,
"options": "Payment Schedule",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:doc.is_pos===1||(doc.advances && doc.advances.length>0)",
@@ -1314,7 +1543,9 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Payments",
- "options": "fa fa-money"
+ "options": "fa fa-money",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "is_pos",
@@ -1327,7 +1558,9 @@
"oldfieldname": "cash_bank_account",
"oldfieldtype": "Link",
"options": "Account",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:doc.is_pos===1",
@@ -1337,13 +1570,17 @@
"hide_seconds": 1,
"label": "Sales Invoice Payment",
"options": "Sales Invoice Payment",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "section_break_84",
"fieldtype": "Section Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_paid_amount",
@@ -1354,13 +1591,17 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_86",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval: doc.is_pos || doc.redeem_loyalty_points",
@@ -1374,13 +1615,17 @@
"oldfieldtype": "Currency",
"options": "currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "section_break_88",
"fieldtype": "Section Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "is_pos",
@@ -1392,13 +1637,17 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_90",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "is_pos",
@@ -1409,7 +1658,9 @@
"label": "Change Amount",
"no_copy": 1,
"options": "currency",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "is_pos",
@@ -1419,7 +1670,9 @@
"hide_seconds": 1,
"label": "Account for Change Amount",
"options": "Account",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1430,6 +1683,8 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Write Off",
+ "show_days": 1,
+ "show_seconds": 1,
"width": "50%"
},
{
@@ -1440,7 +1695,9 @@
"label": "Write Off Amount",
"no_copy": 1,
"options": "currency",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "base_write_off_amount",
@@ -1451,7 +1708,9 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -1461,13 +1720,17 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Write Off Outstanding Amount",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_74",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "write_off_account",
@@ -1476,7 +1739,9 @@
"hide_seconds": 1,
"label": "Write Off Account",
"options": "Account",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "write_off_cost_center",
@@ -1485,7 +1750,9 @@
"hide_seconds": 1,
"label": "Write Off Cost Center",
"options": "Cost Center",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1495,7 +1762,9 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Terms and Conditions",
- "oldfieldtype": "Section Break"
+ "oldfieldtype": "Section Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "tc_name",
@@ -1506,7 +1775,9 @@
"oldfieldname": "tc_name",
"oldfieldtype": "Link",
"options": "Terms and Conditions",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "terms",
@@ -1515,7 +1786,9 @@
"hide_seconds": 1,
"label": "Terms and Conditions Details",
"oldfieldname": "terms",
- "oldfieldtype": "Text Editor"
+ "oldfieldtype": "Text Editor",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1523,7 +1796,9 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Printing Settings"
+ "label": "Printing Settings",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -1535,7 +1810,9 @@
"oldfieldname": "letter_head",
"oldfieldtype": "Select",
"options": "Letter Head",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -1545,7 +1822,9 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Group same items",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "language",
@@ -1554,13 +1833,17 @@
"hide_seconds": 1,
"label": "Print Language",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_84",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -1574,7 +1857,9 @@
"oldfieldtype": "Link",
"options": "Print Heading",
"print_hide": 1,
- "report_hide": 1
+ "report_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1583,7 +1868,9 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "More Information"
+ "label": "More Information",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "inter_company_invoice_reference",
@@ -1592,7 +1879,9 @@
"hide_seconds": 1,
"label": "Inter Company Invoice Reference",
"options": "Purchase Invoice",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "customer_group",
@@ -1602,7 +1891,9 @@
"hide_seconds": 1,
"label": "Customer Group",
"options": "Customer Group",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "campaign",
@@ -1613,7 +1904,9 @@
"oldfieldname": "campaign",
"oldfieldtype": "Link",
"options": "Campaign",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -1623,13 +1916,17 @@
"hide_seconds": 1,
"label": "Is Discounted",
"no_copy": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "col_break23",
"fieldtype": "Column Break",
"hide_days": 1,
"hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1,
"width": "50%"
},
{
@@ -1643,7 +1940,9 @@
"no_copy": 1,
"options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nCancelled\nInternal Transfer",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "source",
@@ -1654,7 +1953,9 @@
"oldfieldname": "source",
"oldfieldtype": "Select",
"options": "Lead Source",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1665,7 +1966,9 @@
"label": "Accounting Details",
"oldfieldtype": "Section Break",
"options": "fa fa-file-text",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "debit_to",
@@ -1678,7 +1981,9 @@
"options": "Account",
"print_hide": 1,
"reqd": 1,
- "search_index": 1
+ "search_index": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "party_account_currency",
@@ -1690,7 +1995,9 @@
"no_copy": 1,
"options": "Currency",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "No",
@@ -1702,7 +2009,9 @@
"oldfieldname": "is_opening",
"oldfieldtype": "Select",
"options": "No\nYes",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "c_form_applicable",
@@ -1712,7 +2021,9 @@
"label": "C-Form Applicable",
"no_copy": 1,
"options": "No\nYes",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "c_form_no",
@@ -1723,7 +2034,9 @@
"no_copy": 1,
"options": "C-Form",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break8",
@@ -1731,7 +2044,9 @@
"hide_days": 1,
"hide_seconds": 1,
"oldfieldtype": "Column Break",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "remarks",
@@ -1742,7 +2057,9 @@
"no_copy": 1,
"oldfieldname": "remarks",
"oldfieldtype": "Text",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1754,7 +2071,9 @@
"label": "Commission",
"oldfieldtype": "Section Break",
"options": "fa fa-group",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "sales_partner",
@@ -1765,7 +2084,9 @@
"oldfieldname": "sales_partner",
"oldfieldtype": "Link",
"options": "Sales Partner",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break10",
@@ -1774,6 +2095,8 @@
"hide_seconds": 1,
"oldfieldtype": "Column Break",
"print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1,
"width": "50%"
},
{
@@ -1784,7 +2107,9 @@
"label": "Commission Rate (%)",
"oldfieldname": "commission_rate",
"oldfieldtype": "Currency",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "total_commission",
@@ -1795,7 +2120,9 @@
"oldfieldname": "total_commission",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1805,7 +2132,9 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Sales Team",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -1817,7 +2146,9 @@
"oldfieldname": "sales_team",
"oldfieldtype": "Table",
"options": "Sales Team",
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1825,7 +2156,9 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Subscription Section"
+ "label": "Subscription Section",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -1835,7 +2168,9 @@
"hide_seconds": 1,
"label": "From Date",
"no_copy": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -1845,13 +2180,17 @@
"hide_seconds": 1,
"label": "To Date",
"no_copy": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_140",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -1863,7 +2202,9 @@
"no_copy": 1,
"options": "Auto Repeat",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
@@ -1872,7 +2213,9 @@
"fieldtype": "Button",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Update Auto Repeat Reference"
+ "label": "Update Auto Repeat Reference",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "against_income_account",
@@ -1885,7 +2228,9 @@
"oldfieldname": "against_income_account",
"oldfieldtype": "Small Text",
"print_hide": 1,
- "report_hide": 1
+ "report_hide": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
@@ -1893,13 +2238,17 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Accounting Dimensions"
+ "label": "Accounting Dimensions",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1
+ "hide_seconds": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -1907,7 +2256,9 @@
"fieldname": "is_consolidated",
"fieldtype": "Check",
"label": "Is Consolidated",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -1917,14 +2268,18 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Is Internal Customer",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fetch_from": "company.tax_id",
"fieldname": "company_tax_id",
"fieldtype": "Data",
"label": "Company Tax ID",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:doc.is_internal_customer",
@@ -1932,7 +2287,9 @@
"fieldname": "unrealized_profit_loss_account",
"fieldtype": "Link",
"label": "Unrealized Profit / Loss Account",
- "options": "Account"
+ "options": "Account",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:doc.is_internal_customer",
@@ -1942,24 +2299,32 @@
"fieldtype": "Link",
"label": "Represents Company",
"options": "Company",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_55",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval: doc.is_internal_customer && doc.update_stock",
"fieldname": "set_target_warehouse",
"fieldtype": "Link",
"label": "Set Target Warehouse",
- "options": "Warehouse"
+ "options": "Warehouse",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
"fieldname": "is_debit_note",
"fieldtype": "Check",
- "label": "Is Debit Note"
+ "label": "Is Debit Note",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -1973,6 +2338,31 @@
"fieldtype": "Link",
"label": "Additional Discount Account",
"options": "Account"
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "dispatch_address_name",
+ "fieldtype": "Link",
+ "label": "Dispatch Address Name",
+ "options": "Address",
+ "print_hide": 1
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "dispatch_address",
+ "fieldtype": "Small Text",
+ "label": "Dispatch Address",
+ "read_only": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "ignore_default_payment_terms_template",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Ignore Default Payment Terms Template",
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
}
],
"icon": "fa fa-file-text",
@@ -1985,7 +2375,7 @@
"link_fieldname": "consolidated_invoice"
}
],
- "modified": "2021-07-15 21:57:17.544279",
+ "modified": "2021-08-06 23:02:20.445127",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 23d3064..1e1fe90 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe, erpnext
import frappe.defaults
-from frappe.utils import cint, flt, getdate, add_days, cstr, nowdate, get_link_to_form, formatdate
+from frappe.utils import cint, flt, getdate, add_days, add_months, cstr, nowdate, get_link_to_form, formatdate
from frappe import _, msgprint, throw
from erpnext.accounts.party import get_party_account, get_due_date, get_party_details
from frappe.model.mapper import get_mapped_doc
@@ -13,7 +13,7 @@
from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so
from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data
from erpnext.assets.doctype.asset.depreciation \
- import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain
+ import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain, post_depreciation_entries
from erpnext.stock.doctype.batch.batch import set_batch_nos
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_delivery_note_serial_no
from erpnext.setup.doctype.company.company import update_company_current_month_sales
@@ -290,6 +290,8 @@
self.update_time_sheet(None)
def on_cancel(self):
+ check_if_return_invoice_linked_with_payment_entry(self)
+
super(SalesInvoice, self).on_cancel()
self.check_sales_order_on_hold_or_close("sales_order")
@@ -480,7 +482,7 @@
if not self.pos_profile:
pos_profile = get_pos_profile(self.company) or {}
if not pos_profile:
- frappe.throw(_("No POS Profile found. Please create a New POS Profile first"))
+ return
self.pos_profile = pos_profile.get('name')
pos = {}
@@ -925,33 +927,30 @@
for item in self.get("items"):
if flt(item.base_net_amount, item.precision("base_net_amount")):
if item.is_fixed_asset:
- if item.get('asset'):
- asset = frappe.get_doc("Asset", item.asset)
- else:
- frappe.throw(_(
- "Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name),
- title=_("Missing Asset")
- )
- if (len(asset.finance_books) > 1 and not item.finance_book
- and asset.finance_books[0].finance_book):
- frappe.throw(_("Select finance book for the item {0} at row {1}")
- .format(item.item_code, item.idx))
+ asset = self.get_asset(item)
if self.is_return:
fixed_asset_gl_entries = get_gl_entries_on_asset_regain(asset,
item.base_net_amount, item.finance_book)
asset.db_set("disposal_date", None)
+
+ if asset.calculate_depreciation:
+ self.reset_depreciation_schedule(asset)
+
else:
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset,
item.base_net_amount, item.finance_book)
asset.db_set("disposal_date", self.posting_date)
+ if asset.calculate_depreciation:
+ self.depreciate_asset(asset)
+
for gle in fixed_asset_gl_entries:
gle["against"] = self.customer
gl_entries.append(self.get_gl_dict(gle, item=item))
self.set_asset_status(asset)
-
+
else:
# Do not book income for transfer within same company
if not self.is_internal_transfer():
@@ -979,10 +978,93 @@
erpnext.is_perpetual_inventory_enabled(self.company):
gl_entries += super(SalesInvoice, self).get_gl_entries()
+ def get_asset(self, item):
+ if item.get('asset'):
+ asset = frappe.get_doc("Asset", item.asset)
+ else:
+ frappe.throw(_(
+ "Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name),
+ title=_("Missing Asset")
+ )
+
+ self.check_finance_books(item, asset)
+ return asset
+
+ def check_finance_books(self, item, asset):
+ if (len(asset.finance_books) > 1 and not item.finance_book
+ and asset.finance_books[0].finance_book):
+ frappe.throw(_("Select finance book for the item {0} at row {1}")
+ .format(item.item_code, item.idx))
+
+ def depreciate_asset(self, asset):
+ asset.flags.ignore_validate_update_after_submit = True
+ asset.prepare_depreciation_data(self.posting_date)
+ asset.save()
+
+ post_depreciation_entries(self.posting_date)
+
+ def reset_depreciation_schedule(self, asset):
+ asset.flags.ignore_validate_update_after_submit = True
+
+ # recreate original depreciation schedule of the asset
+ asset.prepare_depreciation_data()
+
+ self.modify_depreciation_schedule_for_asset_repairs(asset)
+ asset.save()
+
+ self.delete_depreciation_entry_made_after_sale(asset)
+
+ def modify_depreciation_schedule_for_asset_repairs(self, asset):
+ asset_repairs = frappe.get_all(
+ 'Asset Repair',
+ filters = {'asset': asset.name},
+ fields = ['name', 'increase_in_asset_life']
+ )
+
+ for repair in asset_repairs:
+ if repair.increase_in_asset_life:
+ asset_repair = frappe.get_doc('Asset Repair', repair.name)
+ asset_repair.modify_depreciation_schedule()
+ asset.prepare_depreciation_data()
+
+ def delete_depreciation_entry_made_after_sale(self, asset):
+ from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
+
+ posting_date_of_original_invoice = self.get_posting_date_of_sales_invoice()
+
+ row = -1
+ finance_book = asset.get('schedules')[0].get('finance_book')
+ for schedule in asset.get('schedules'):
+ if schedule.finance_book != finance_book:
+ row = 0
+ finance_book = schedule.finance_book
+ else:
+ row += 1
+
+ if schedule.schedule_date == posting_date_of_original_invoice:
+ if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice):
+ reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
+ reverse_journal_entry.posting_date = nowdate()
+ reverse_journal_entry.submit()
+
+ def get_posting_date_of_sales_invoice(self):
+ return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date')
+
+ # if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone
+ def sale_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_original_invoice):
+ for finance_book in asset.get('finance_books'):
+ if schedule.finance_book == finance_book.finance_book:
+ orginal_schedule_date = add_months(finance_book.depreciation_start_date,
+ row * cint(finance_book.frequency_of_depreciation))
+
+ if orginal_schedule_date == posting_date_of_original_invoice:
+ return True
+ return False
+
def set_asset_status(self, asset):
if self.is_return:
asset.set_status()
- else:
+ else:
asset.set_status("Sold" if self.docstatus==1 else None)
def make_loyalty_point_redemption_gle(self, gl_entries):
@@ -1950,3 +2032,41 @@
}
}, target_doc, set_missing_values)
return doclist
+
+def check_if_return_invoice_linked_with_payment_entry(self):
+ # If a Return invoice is linked with payment entry along with other invoices,
+ # the cancellation of the Return causes allocated amount to be greater than paid
+
+ if not frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
+ return
+
+ payment_entries = []
+ if self.is_return and self.return_against:
+ invoice = self.return_against
+ else:
+ invoice = self.name
+
+ payment_entries = frappe.db.sql_list("""
+ SELECT
+ t1.name
+ FROM
+ `tabPayment Entry` t1, `tabPayment Entry Reference` t2
+ WHERE
+ t1.name = t2.parent
+ and t1.docstatus = 1
+ and t2.reference_name = %s
+ and t2.allocated_amount < 0
+ """, invoice)
+
+ links_to_pe = []
+ if payment_entries:
+ for payment in payment_entries:
+ payment_entry = frappe.get_doc("Payment Entry", payment)
+ if len(payment_entry.references) > 1:
+ links_to_pe.append(payment_entry.name)
+ if links_to_pe:
+ payment_entries_link = [get_link_to_form('Payment Entry', name, label=name) for name in links_to_pe]
+ message = _("Please cancel and amend the Payment Entry")
+ message += " " + ", ".join(payment_entries_link) + " "
+ message += _("to unallocate the amount of this Return Invoice before cancelling it.")
+ frappe.throw(message)
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index bde11d2..54a8852 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -2,15 +2,17 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+import frappe, erpnext
import unittest, copy, time
from frappe.utils import nowdate, flt, getdate, cint, add_days, add_months
from frappe.model.dynamic_links import get_dynamic_link_map
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
+from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import WarehouseMissingError
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_data
+from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError
from frappe.model.naming import make_autoname
@@ -1073,7 +1075,7 @@
def test_gle_made_when_asset_is_returned(self):
create_asset_data()
asset = create_asset(item_code="Macbook Pro")
-
+
si = create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000)
return_si = create_sales_invoice(is_return=1, return_against=si.name, item_code="Macbook Pro", asset=asset.name, qty=-1, rate=90000)
@@ -1081,7 +1083,7 @@
# Asset value is 100,000 but it was sold for 90,000, so there should be a loss of 10,000
loss_for_si = frappe.get_all(
- "GL Entry",
+ "GL Entry",
filters = {
"voucher_no": si.name,
"account": disposal_account
@@ -1090,7 +1092,7 @@
)[0]
loss_for_return_si = frappe.get_all(
- "GL Entry",
+ "GL Entry",
filters = {
"voucher_no": return_si.name,
"account": disposal_account
@@ -1836,6 +1838,89 @@
self.assertEqual(target_doc.company, "_Test Company 1")
self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
+ def test_inter_company_transaction_without_default_warehouse(self):
+ "Check mapping (expense account) of inter company SI to PI in absence of default warehouse."
+ # setup
+ old_negative_stock = frappe.db.get_single_value("Stock Settings", "allow_negative_stock")
+ frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
+
+ old_perpetual_inventory = erpnext.is_perpetual_inventory_enabled('_Test Company 1')
+ frappe.local.enable_perpetual_inventory['_Test Company 1'] = 1
+
+ frappe.db.set_value("Company", '_Test Company 1', "stock_received_but_not_billed", "Stock Received But Not Billed - _TC1")
+ frappe.db.set_value("Company", '_Test Company 1', "expenses_included_in_valuation", "Expenses Included In Valuation - _TC1")
+
+
+ if not frappe.db.exists("Customer", "_Test Internal Customer"):
+ customer = frappe.get_doc({
+ "customer_group": "_Test Customer Group",
+ "customer_name": "_Test Internal Customer",
+ "customer_type": "Individual",
+ "doctype": "Customer",
+ "territory": "_Test Territory",
+ "is_internal_customer": 1,
+ "represents_company": "_Test Company 1"
+ })
+
+ customer.append("companies", {
+ "company": "Wind Power LLC"
+ })
+
+ customer.insert()
+
+ if not frappe.db.exists("Supplier", "_Test Internal Supplier"):
+ supplier = frappe.get_doc({
+ "supplier_group": "_Test Supplier Group",
+ "supplier_name": "_Test Internal Supplier",
+ "doctype": "Supplier",
+ "is_internal_supplier": 1,
+ "represents_company": "Wind Power LLC"
+ })
+
+ supplier.append("companies", {
+ "company": "_Test Company 1"
+ })
+
+ supplier.insert()
+
+ # begin test
+ si = create_sales_invoice(
+ company = "Wind Power LLC",
+ customer = "_Test Internal Customer",
+ debit_to = "Debtors - WP",
+ warehouse = "Stores - WP",
+ income_account = "Sales - WP",
+ expense_account = "Cost of Goods Sold - WP",
+ cost_center = "Main - WP",
+ currency = "USD",
+ update_stock = 1,
+ do_not_save = 1
+ )
+ si.selling_price_list = "_Test Price List Rest of the World"
+ si.submit()
+
+ target_doc = make_inter_company_transaction("Sales Invoice", si.name)
+
+ # in absence of warehouse Stock Received But Not Billed is set as expense account while mapping
+ # mapping is not obstructed
+ self.assertIsNone(target_doc.items[0].warehouse)
+ self.assertEqual(target_doc.items[0].expense_account, "Stock Received But Not Billed - _TC1")
+
+ target_doc.items[0].update({"cost_center": "Main - _TC1"})
+
+ # missing warehouse is validated on save, after mapping
+ self.assertRaises(WarehouseMissingError, target_doc.save)
+
+ target_doc.items[0].update({"warehouse": "Stores - _TC1"})
+ target_doc.save()
+
+ # after warehouse is set, linked account or default inventory account is set
+ self.assertEqual(target_doc.items[0].expense_account, 'Stock In Hand - _TC1')
+
+ # tear down
+ frappe.local.enable_perpetual_inventory['_Test Company 1'] = old_perpetual_inventory
+ frappe.db.set_value("Stock Settings", None, "allow_negative_stock", old_negative_stock)
+
def test_internal_transfer_gl_entry(self):
## Create internal transfer account
account = create_account(account_name="Unrealized Profit",
@@ -1939,6 +2024,8 @@
self.assertEqual(data['billLists'][0]['sgstValue'], 5400)
self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234')
self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000)
+ self.assertEqual(data['billLists'][0]['actualFromStateCode'],7)
+ self.assertEqual(data['billLists'][0]['fromStateCode'],27)
def test_einvoice_submission_without_irn(self):
# init
@@ -2063,6 +2150,30 @@
check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
enable_discount_accounting(enable=0)
+ def test_asset_depreciation_on_sale(self):
+ """
+ Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on Sept 30.
+ """
+
+ create_asset_data()
+ asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, submit=1)
+ post_depreciation_entries(getdate("2021-09-30"))
+
+ create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-09-30"))
+ asset.load_from_db()
+
+ expected_values = [
+ ["2020-06-30", 1311.48, 1311.48],
+ ["2021-06-30", 20000.0, 21311.48],
+ ["2021-09-30", 3966.76, 25278.24]
+ ]
+
+ for i, schedule in enumerate(asset.schedules):
+ self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
+ self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
+ self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
+ self.assertTrue(schedule.journal_entry)
+
def get_sales_invoice_for_e_invoice():
si = make_sales_invoice_for_ewaybill()
si.naming_series = 'INV-2020-.#####'
@@ -2140,6 +2251,30 @@
address.save()
+ if not frappe.db.exists('Address', '_Test Dispatch-Address for Eway bill-Shipping'):
+ address = frappe.get_doc({
+ "address_line1": "_Test Dispatch Address Line 1",
+ "address_title": "_Test Dispatch-Address for Eway bill",
+ "address_type": "Shipping",
+ "city": "_Test City",
+ "state": "Test State",
+ "country": "India",
+ "doctype": "Address",
+ "is_primary_address": 0,
+ "phone": "+910000000000",
+ "gstin": "07AAACC1206D1ZI",
+ "gst_state": "Delhi",
+ "gst_state_number": "07",
+ "pincode": "1100101"
+ }).insert()
+
+ address.append("links", {
+ "link_doctype": "Company",
+ "link_name": "_Test Company"
+ })
+
+ address.save()
+
def make_test_transporter_for_ewaybill():
if not frappe.db.exists('Supplier', '_Test Transporter'):
frappe.get_doc({
@@ -2178,6 +2313,7 @@
si.distance = 2000
si.company_address = "_Test Address for Eway bill-Billing"
si.customer_address = "_Test Customer-Address for Eway bill-Shipping"
+ si.dispatch_address_name = "_Test Dispatch-Address for Eway bill-Shipping"
si.vehicle_no = "KA12KA1234"
si.gst_category = "Registered Regular"
si.mode_of_transport = 'Road'
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index 0da439e..a853679 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -474,6 +474,7 @@
},
{
"collapsible": 1,
+ "collapsible_depends_on": "enable_deferred_revenue",
"fieldname": "deferred_revenue",
"fieldtype": "Section Break",
"label": "Deferred Revenue"
@@ -832,7 +833,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2021-07-05 15:07:22.857128",
+ "modified": "2021-08-12 20:15:42.668399",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json
index 1b7a0fe..3a871bf 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json
@@ -19,7 +19,7 @@
"section_break_8",
"rate",
"section_break_9",
- "currency",
+ "account_currency",
"tax_amount",
"total",
"tax_amount_after_discount_amount",
@@ -27,7 +27,8 @@
"base_tax_amount",
"base_total",
"base_tax_amount_after_discount_amount",
- "item_wise_tax_detail"
+ "item_wise_tax_detail",
+ "dont_recompute_tax"
],
"fields": [
{
@@ -186,27 +187,36 @@
"fieldtype": "Column Break"
},
{
- "fetch_from": "account_head.account_currency",
- "fieldname": "currency",
- "fieldtype": "Link",
- "label": "Account Currency",
- "options": "Currency",
- "read_only": 1
- },
- {
"default": "0",
"depends_on": "eval:['Sales Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)",
"description": "If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry",
"fieldname": "included_in_paid_amount",
"fieldtype": "Check",
"label": "Considered In Paid Amount"
+ },
+ {
+ "default": "0",
+ "fieldname": "dont_recompute_tax",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Dont Recompute tax",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fetch_from": "account_head.account_currency",
+ "fieldname": "account_currency",
+ "fieldtype": "Link",
+ "label": "Account Currency",
+ "options": "Currency",
+ "read_only": 1
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-06-14 01:44:36.899147",
+ "modified": "2021-08-05 20:04:01.726867",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Taxes and Charges",
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
index 52d19d5..8f9eb65 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
@@ -6,7 +6,7 @@
from frappe import _
from frappe.utils import flt
from frappe.model.document import Document
-from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax
+from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax, validate_cost_center, validate_account_head
class SalesTaxesandChargesTemplate(Document):
def validate(self):
@@ -39,6 +39,8 @@
for tax in doc.get("taxes"):
validate_taxes_and_charges(tax)
+ validate_account_head(tax, doc)
+ validate_cost_center(tax, doc)
validate_inclusive_tax(tax, doc)
def validate_disabled(doc):
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json
index 2b737b9..74db08d 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json
@@ -8,6 +8,7 @@
"charge_type": "On Net Total",
"description": "VAT",
"doctype": "Sales Taxes and Charges",
+ "cost_center": "Main - _TC",
"parentfield": "taxes",
"rate": 6
},
@@ -16,6 +17,7 @@
"charge_type": "On Net Total",
"description": "Service Tax",
"doctype": "Sales Taxes and Charges",
+ "cost_center": "Main - _TC",
"parentfield": "taxes",
"rate": 6.36
}
@@ -114,6 +116,7 @@
"charge_type": "On Net Total",
"description": "VAT",
"doctype": "Sales Taxes and Charges",
+ "cost_center": "Main - _TC",
"parentfield": "taxes",
"rate": 12
},
@@ -122,6 +125,7 @@
"charge_type": "On Net Total",
"description": "Service Tax",
"doctype": "Sales Taxes and Charges",
+ "cost_center": "Main - _TC",
"parentfield": "taxes",
"rate": 4
}
@@ -137,6 +141,7 @@
"charge_type": "On Net Total",
"description": "VAT",
"doctype": "Sales Taxes and Charges",
+ "cost_center": "Main - _TC",
"parentfield": "taxes",
"rate": 12
},
@@ -145,6 +150,7 @@
"charge_type": "On Net Total",
"description": "Service Tax",
"doctype": "Sales Taxes and Charges",
+ "cost_center": "Main - _TC",
"parentfield": "taxes",
"rate": 4
}
@@ -160,6 +166,7 @@
"charge_type": "On Net Total",
"description": "VAT",
"doctype": "Sales Taxes and Charges",
+ "cost_center": "Main - _TC",
"parentfield": "taxes",
"rate": 12
},
@@ -168,6 +175,7 @@
"charge_type": "On Net Total",
"description": "Service Tax",
"doctype": "Sales Taxes and Charges",
+ "cost_center": "Main - _TC",
"parentfield": "taxes",
"rate": 4
}
diff --git a/erpnext/erpnext_integrations/doctype/shopify_tax_account/__init__.py b/erpnext/accounts/doctype/south_africa_vat_account/__init__.py
similarity index 100%
rename from erpnext/erpnext_integrations/doctype/shopify_tax_account/__init__.py
rename to erpnext/accounts/doctype/south_africa_vat_account/__init__.py
diff --git a/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.json b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.json
new file mode 100644
index 0000000..fa1aa7d
--- /dev/null
+++ b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.json
@@ -0,0 +1,34 @@
+{
+ "actions": [],
+ "autoname": "account",
+ "creation": "2021-07-08 22:04:24.634967",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "account"
+ ],
+ "fields": [
+ {
+ "allow_in_quick_entry": 1,
+ "fieldname": "account",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_preview": 1,
+ "label": "Account",
+ "options": "Account"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-07-08 22:35:33.202911",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "South Africa VAT Account",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py
new file mode 100644
index 0000000..4bd8c65
--- /dev/null
+++ b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+class SouthAfricaVATAccount(Document):
+ pass
diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.json b/erpnext/accounts/doctype/subscription_plan/subscription_plan.json
index 46ce093..771611a 100644
--- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.json
+++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.json
@@ -78,7 +78,7 @@
"label": "Cost"
},
{
- "depends_on": "eval:doc.price_determination==\"Based on price list\"",
+ "depends_on": "eval:doc.price_determination==\"Based On Price List\"",
"fieldname": "price_list",
"fieldtype": "Link",
"label": "Price List",
@@ -147,7 +147,7 @@
}
],
"links": [],
- "modified": "2020-06-25 10:53:44.205774",
+ "modified": "2021-08-09 10:53:44.205774",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Subscription Plan",
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json
index f9160e2..153906f 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json
@@ -1,263 +1,151 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "Prompt",
- "beta": 0,
- "creation": "2018-04-13 18:42:06.431683",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "actions": [],
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "Prompt",
+ "creation": "2018-04-13 18:42:06.431683",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "category_details_section",
+ "category_name",
+ "round_off_tax_amount",
+ "column_break_2",
+ "consider_party_ledger_amount",
+ "tax_on_excess_amount",
+ "section_break_8",
+ "rates",
+ "section_break_7",
+ "accounts"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "category_name",
"fieldtype": "Data",
- "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": "Category Name",
- "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
- },
+ "show_days": 1,
+ "show_seconds": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "section_break_8",
"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,
"label": "Tax Withholding Rates",
- "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
+ "show_days": 1,
+ "show_seconds": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "rates",
"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": "Rates",
- "length": 0,
- "no_copy": 0,
"options": "Tax Withholding Rate",
- "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,
- "translatable": 0,
- "unique": 0
+ "show_days": 1,
+ "show_seconds": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_7",
- "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,
+ "fieldname": "section_break_7",
+ "fieldtype": "Section Break",
"label": "Account 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,
- "translatable": 0,
- "unique": 0
- },
+ "show_days": 1,
+ "show_seconds": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "accounts",
- "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": "Accounts",
- "length": 0,
- "no_copy": 0,
- "options": "Tax Withholding Account",
- "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,
- "translatable": 0,
- "unique": 0
+ "fieldname": "accounts",
+ "fieldtype": "Table",
+ "label": "Accounts",
+ "options": "Tax Withholding Account",
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
+ },
+ {
+ "fieldname": "category_details_section",
+ "fieldtype": "Section Break",
+ "label": "Category Details",
+ "show_days": 1,
+ "show_seconds": 1
+ },
+ {
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
+ },
+ {
+ "default": "0",
+ "description": "Even invoices with apply tax withholding unchecked will be considered for checking cumulative threshold breach",
+ "fieldname": "consider_party_ledger_amount",
+ "fieldtype": "Check",
+ "label": "Consider Entire Party Ledger Amount",
+ "show_days": 1,
+ "show_seconds": 1
+ },
+ {
+ "default": "0",
+ "description": "Tax will be withheld only for amount exceeding the cumulative threshold",
+ "fieldname": "tax_on_excess_amount",
+ "fieldtype": "Check",
+ "label": "Only Deduct Tax On Excess Amount ",
+ "show_days": 1,
+ "show_seconds": 1
+ },
+ {
+ "description": "Checking this will round off the tax amount to the nearest integer",
+ "fieldname": "round_off_tax_amount",
+ "fieldtype": "Check",
+ "label": "Round Off Tax Amount",
+ "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": 0,
- "max_attachments": 0,
- "modified": "2018-07-17 22:53:26.193179",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Tax Withholding Category",
- "name_case": "",
- "owner": "Administrator",
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-07-27 21:47:34.396071",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Tax Withholding Category",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
"write": 1
- },
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Accounts Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts Manager",
+ "share": 1,
"write": 1
- },
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Accounts User",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "share": 1,
"write": 1
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index b9ee4a0..481ef28 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -6,7 +6,7 @@
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import flt, getdate
+from frappe.utils import flt, getdate, cint
from erpnext.accounts.utils import get_fiscal_year
class TaxWithholdingCategory(Document):
@@ -86,7 +86,10 @@
"rate": tax_rate_detail.tax_withholding_rate,
"threshold": tax_rate_detail.single_threshold,
"cumulative_threshold": tax_rate_detail.cumulative_threshold,
- "description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category
+ "description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category,
+ "consider_party_ledger_amount": tax_withholding.consider_party_ledger_amount,
+ "tax_on_excess_amount": tax_withholding.tax_on_excess_amount,
+ "round_off_tax_amount": tax_withholding.round_off_tax_amount
})
def get_tax_withholding_rates(tax_withholding, fiscal_year):
@@ -145,6 +148,7 @@
def get_tax_amount(party_type, parties, inv, tax_details, fiscal_year_details, pan_no=None):
fiscal_year = fiscal_year_details[0]
+
vouchers = get_invoice_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
advance_vouchers = get_advance_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
taxable_vouchers = vouchers + advance_vouchers
@@ -235,10 +239,18 @@
def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_deducted, vouchers):
tds_amount = 0
+ invoice_filters = {
+ 'name': ('in', vouchers),
+ 'docstatus': 1
+ }
- supp_credit_amt = frappe.db.get_value('Purchase Invoice', {
- 'name': ('in', vouchers), 'docstatus': 1, 'apply_tds': 1
- }, 'sum(net_total)') or 0.0
+ field = 'sum(net_total)'
+
+ if not cint(tax_details.consider_party_ledger_amount):
+ invoice_filters.update({'apply_tds': 1})
+ field = 'sum(grand_total)'
+
+ supp_credit_amt = frappe.db.get_value('Purchase Invoice', invoice_filters, field) or 0.0
supp_jv_credit_amt = frappe.db.get_value('Journal Entry Account', {
'parent': ('in', vouchers), 'docstatus': 1,
@@ -255,6 +267,13 @@
cumulative_threshold = tax_details.get('cumulative_threshold', 0)
if ((threshold and inv.net_total >= threshold) or (cumulative_threshold and supp_credit_amt >= cumulative_threshold)):
+ if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(tax_details.tax_on_excess_amount):
+ # Get net total again as TDS is calculated on net total
+ # Grand is used to just check for threshold breach
+ net_total = frappe.db.get_value('Purchase Invoice', invoice_filters, 'sum(net_total)') or 0.0
+ net_total += inv.net_total
+ supp_credit_amt = net_total - cumulative_threshold
+
if ldc and is_valid_certificate(
ldc.valid_from, ldc.valid_upto,
inv.get('posting_date') or inv.get('transaction_date'), tax_deducted,
@@ -263,6 +282,9 @@
tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
else:
tds_amount = supp_credit_amt * tax_details.rate / 100 if supp_credit_amt > 0 else 0
+
+ if cint(tax_details.round_off_tax_amount):
+ tds_amount = round(tds_amount)
return tds_amount
diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
index dd26be7..2ba22ca 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
@@ -87,6 +87,31 @@
for d in invoices:
d.cancel()
+ def test_tax_withholding_category_checks(self):
+ invoices = []
+ frappe.db.set_value("Supplier", "Test TDS Supplier3", "tax_withholding_category", "New TDS Category")
+
+ # First Invoice with no tds check
+ pi = create_purchase_invoice(supplier = "Test TDS Supplier3", rate = 20000, do_not_save=True)
+ pi.apply_tds = 0
+ pi.save()
+ pi.submit()
+ invoices.append(pi)
+
+ # Second Invoice will apply TDS checked
+ pi1 = create_purchase_invoice(supplier = "Test TDS Supplier3", rate = 20000)
+ pi1.submit()
+ invoices.append(pi1)
+
+ # Cumulative threshold is 30000
+ # Threshold calculation should be on both the invoices
+ # TDS should be applied only on 1000
+ self.assertEqual(pi1.taxes[0].tax_amount, 1000)
+
+ for d in invoices:
+ d.cancel()
+
+
def test_cumulative_threshold_tcs(self):
frappe.db.set_value("Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS")
invoices = []
@@ -195,7 +220,7 @@
def create_records():
# create a new suppliers
- for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']:
+ for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2', 'Test TDS Supplier3']:
if frappe.db.exists('Supplier', name):
continue
@@ -311,3 +336,23 @@
'account': 'TDS - _TC'
}]
}).insert()
+
+ if not frappe.db.exists("Tax Withholding Category", "New TDS Category"):
+ frappe.get_doc({
+ "doctype": "Tax Withholding Category",
+ "name": "New TDS Category",
+ "category_name": "New TDS Category",
+ "round_off_tax_amount": 1,
+ "consider_party_ledger_amount": 1,
+ "tax_on_excess_amount": 1,
+ "rates": [{
+ 'fiscal_year': fiscal_year,
+ 'tax_withholding_rate': 10,
+ 'single_threshold': 0,
+ 'cumulative_threshold': 30000
+ }],
+ "accounts": [{
+ 'company': '_Test Company',
+ 'account': 'TDS - _TC'
+ }]
+ }).insert()
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 25d2cf1..4c7c567 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -100,8 +100,8 @@
return merged_gl_map
def check_if_in_list(gle, gl_map, dimensions=None):
- account_head_fieldnames = ['party_type', 'party', 'against_voucher', 'against_voucher_type',
- 'cost_center', 'project', 'voucher_detail_no']
+ account_head_fieldnames = ['voucher_detail_no', 'party', 'against_voucher',
+ 'cost_center', 'against_voucher_type', 'party_type', 'project']
if dimensions:
account_head_fieldnames = account_head_fieldnames + dimensions
@@ -110,10 +110,12 @@
same_head = True
if e.account != gle.account:
same_head = False
+ continue
for fieldname in account_head_fieldnames:
if cstr(e.get(fieldname)) != cstr(gle.get(fieldname)):
same_head = False
+ break
if same_head:
return e
@@ -143,16 +145,19 @@
validate_expense_against_budget(args)
def validate_cwip_accounts(gl_map):
- cwip_enabled = any(cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting"))
+ """Validate that CWIP account are not used in Journal Entry"""
+ if gl_map and gl_map[0].voucher_type != "Journal Entry":
+ return
- if cwip_enabled and gl_map[0].voucher_type == "Journal Entry":
- cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
- where account_type = 'Capital Work in Progress' and is_group=0""")]
+ cwip_enabled = any(cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category", "enable_cwip_accounting"))
+ if cwip_enabled:
+ cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
+ where account_type = 'Capital Work in Progress' and is_group=0""")]
- for entry in gl_map:
- if entry.account in cwip_accounts:
- frappe.throw(
- _("Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry").format(entry.account))
+ for entry in gl_map:
+ if entry.account in cwip_accounts:
+ frappe.throw(
+ _("Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry").format(entry.account))
def round_off_debit_credit(gl_map):
precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index b97dc40..329f9a9 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -8,7 +8,7 @@
from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
from frappe.model.utils import get_fetch_values
from frappe.utils import (add_days, getdate, formatdate, date_diff,
- add_years, get_timestamp, nowdate, flt, cstr, add_months, get_last_day)
+ add_years, get_timestamp, nowdate, flt, cstr, add_months, get_last_day, cint)
from frappe.contacts.doctype.address.address import (get_address_display,
get_default_address, get_company_address)
from frappe.contacts.doctype.contact.contact import get_contact_details
@@ -58,7 +58,7 @@
customer_group=party_details.customer_group, supplier_group=party_details.supplier_group, tax_category=party_details.tax_category,
billing_address=party_address, shipping_address=shipping_address)
- if fetch_payment_terms_template:
+ if cint(fetch_payment_terms_template):
party_details["payment_terms_template"] = get_payment_terms_template(party.name, party_type, company)
if not party_details.get("currency"):
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index a11b77a..b54646f 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -99,7 +99,6 @@
voucher_no = gle.voucher_no,
party = gle.party,
posting_date = gle.posting_date,
- remarks = gle.remarks,
account_currency = gle.account_currency,
invoiced = 0.0,
paid = 0.0,
@@ -579,7 +578,7 @@
self.gl_entries = frappe.db.sql("""
select
name, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center,
- against_voucher_type, against_voucher, account_currency, remarks, {0}
+ against_voucher_type, against_voucher, account_currency, {0}
from
`tabGL Entry`
where
@@ -792,8 +791,6 @@
self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link',
options='Supplier Group')
- self.add_column(label=_('Remarks'), fieldname='remarks', fieldtype='Text', width=200)
-
def add_column(self, label, fieldname=None, fieldtype='Currency', options=None, width=120):
if not fieldname: fieldname = scrub(label)
if fieldtype=='Currency': options='currency'
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index 84c7454..6d8623c 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -241,6 +241,7 @@
sle.voucher_detail_no == row.item_row:
previous_stock_value = len(my_sle) > i+1 and \
flt(my_sle[i+1].stock_value) or 0.0
+
if previous_stock_value:
return (previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
else:
@@ -335,7 +336,7 @@
res = frappe.db.sql("""select item_code, voucher_type, voucher_no,
voucher_detail_no, stock_value, warehouse, actual_qty as qty
from `tabStock Ledger Entry`
- where company=%(company)s
+ where company=%(company)s and is_cancelled = 0
order by
item_code desc, warehouse desc, posting_date desc,
posting_time desc, creation desc""", self.filters, as_dict=True)
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 1cdbd8d..5b58e87 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -566,10 +566,10 @@
frappe.msgprint(_("Payment Entries {0} are un-linked").format("\n".join(linked_pe)))
@frappe.whitelist()
-def get_company_default(company, fieldname):
- value = frappe.get_cached_value('Company', company, fieldname)
+def get_company_default(company, fieldname, ignore_validation=False):
+ value = frappe.get_cached_value('Company', company, fieldname)
- if not value:
+ if not ignore_validation and not value:
throw(_("Please set default {0} in Company {1}")
.format(frappe.get_meta("Company").get_label(fieldname), company))
@@ -920,7 +920,6 @@
_delete_gl_entries(voucher_type, voucher_no)
def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None, company=None):
- future_stock_vouchers = []
values = []
condition = ""
@@ -936,37 +935,53 @@
condition += " and company = %s"
values.append(company)
- for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
+ future_stock_vouchers = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
from `tabStock Ledger Entry` sle
where
timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s)
and is_cancelled = 0
{condition}
order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc for update""".format(condition=condition),
- tuple([posting_date, posting_time] + values), as_dict=True):
- future_stock_vouchers.append([d.voucher_type, d.voucher_no])
+ tuple([posting_date, posting_time] + values), as_dict=True)
- return future_stock_vouchers
+ return [(d.voucher_type, d.voucher_no) for d in future_stock_vouchers]
def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
+ """ Get voucherwise list of GL entries.
+
+ Only fetches GLE fields required for comparing with new GLE.
+ Check compare_existing_and_expected_gle function below.
+ """
gl_entries = {}
- if future_stock_vouchers:
- for d in frappe.db.sql("""select * from `tabGL Entry`
- where posting_date >= %s and voucher_no in (%s)""" %
- ('%s', ', '.join(['%s']*len(future_stock_vouchers))),
- tuple([posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
- gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
+ if not future_stock_vouchers:
+ return gl_entries
+
+ voucher_nos = [d[1] for d in future_stock_vouchers]
+
+ gles = frappe.db.sql("""
+ select name, account, credit, debit, cost_center, project
+ from `tabGL Entry`
+ where
+ posting_date >= %s and voucher_no in (%s)""" %
+ ('%s', ', '.join(['%s'] * len(voucher_nos))),
+ tuple([posting_date] + voucher_nos), as_dict=1)
+
+ for d in gles:
+ gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
return gl_entries
def compare_existing_and_expected_gle(existing_gle, expected_gle, precision):
+ if len(existing_gle) != len(expected_gle):
+ return False
+
matched = True
for entry in expected_gle:
account_existed = False
for e in existing_gle:
if entry.account == e.account:
account_existed = True
- if (entry.account == e.account and entry.against_account == e.against_account
+ if (entry.account == e.account
and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center)
and ( flt(entry.debit, precision) != flt(e.debit, precision) or
flt(entry.credit, precision) != flt(e.credit, precision))):
diff --git a/erpnext/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json
index 821fa4d..b5bd14d 100644
--- a/erpnext/accounts/workspace/accounting/accounting.json
+++ b/erpnext/accounts/workspace/accounting/accounting.json
@@ -1,28 +1,32 @@
{
- "category": "Modules",
+ "category": "",
"charts": [
{
"chart_name": "Profit and Loss",
"label": "Profit and Loss"
}
],
+ "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Accounts\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Profit and Loss\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Chart of Accounts\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Invoice\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Invoice\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Journal Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Payment Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Accounts Receivable\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"General Ledger\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Trial Balance\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Accounting Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"General Ledger\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Accounts Receivable\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Accounts Payable\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Financial Statements\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Multi Currency\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Bank Statement\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Subscription Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Goods and Services Tax (GST India)\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Share Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Cost Center and Budgeting\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Opening and Closing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Taxes\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Profitability\", \"col\": 4}}]",
"creation": "2020-03-02 15:41:59.515192",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "accounting",
"idx": 0,
"is_default": 0,
- "is_standard": 1,
+ "is_standard": 0,
"label": "Accounting",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Accounting Masters",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -31,6 +35,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Company",
+ "link_count": 0,
"link_to": "Company",
"link_type": "DocType",
"onboard": 1,
@@ -41,6 +46,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Chart of Accounts",
+ "link_count": 0,
"link_to": "Account",
"link_type": "DocType",
"onboard": 1,
@@ -51,6 +57,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Accounts Settings",
+ "link_count": 0,
"link_to": "Accounts Settings",
"link_type": "DocType",
"onboard": 0,
@@ -61,6 +68,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Fiscal Year",
+ "link_count": 0,
"link_to": "Fiscal Year",
"link_type": "DocType",
"onboard": 0,
@@ -71,6 +79,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Accounting Dimension",
+ "link_count": 0,
"link_to": "Accounting Dimension",
"link_type": "DocType",
"onboard": 0,
@@ -81,6 +90,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Finance Book",
+ "link_count": 0,
"link_to": "Finance Book",
"link_type": "DocType",
"onboard": 0,
@@ -91,6 +101,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Accounting Period",
+ "link_count": 0,
"link_to": "Accounting Period",
"link_type": "DocType",
"onboard": 0,
@@ -101,6 +112,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Payment Term",
+ "link_count": 0,
"link_to": "Payment Term",
"link_type": "DocType",
"onboard": 0,
@@ -110,6 +122,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "General Ledger",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -118,6 +131,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Journal Entry",
+ "link_count": 0,
"link_to": "Journal Entry",
"link_type": "DocType",
"onboard": 0,
@@ -128,6 +142,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Journal Entry Template",
+ "link_count": 0,
"link_to": "Journal Entry Template",
"link_type": "DocType",
"onboard": 0,
@@ -138,6 +153,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "General Ledger",
+ "link_count": 0,
"link_to": "General Ledger",
"link_type": "Report",
"onboard": 0,
@@ -148,6 +164,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Customer Ledger Summary",
+ "link_count": 0,
"link_to": "Customer Ledger Summary",
"link_type": "Report",
"onboard": 0,
@@ -158,6 +175,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Supplier Ledger Summary",
+ "link_count": 0,
"link_to": "Supplier Ledger Summary",
"link_type": "Report",
"onboard": 0,
@@ -167,6 +185,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Accounts Receivable",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -175,6 +194,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Sales Invoice",
+ "link_count": 0,
"link_to": "Sales Invoice",
"link_type": "DocType",
"onboard": 1,
@@ -185,6 +205,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Customer",
+ "link_count": 0,
"link_to": "Customer",
"link_type": "DocType",
"onboard": 1,
@@ -195,6 +216,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Payment Entry",
+ "link_count": 0,
"link_to": "Payment Entry",
"link_type": "DocType",
"onboard": 0,
@@ -205,6 +227,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Payment Request",
+ "link_count": 0,
"link_to": "Payment Request",
"link_type": "DocType",
"onboard": 0,
@@ -215,6 +238,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Accounts Receivable",
+ "link_count": 0,
"link_to": "Accounts Receivable",
"link_type": "Report",
"onboard": 0,
@@ -225,6 +249,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Accounts Receivable Summary",
+ "link_count": 0,
"link_to": "Accounts Receivable Summary",
"link_type": "Report",
"onboard": 0,
@@ -235,6 +260,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Sales Register",
+ "link_count": 0,
"link_to": "Sales Register",
"link_type": "Report",
"onboard": 0,
@@ -245,6 +271,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Item-wise Sales Register",
+ "link_count": 0,
"link_to": "Item-wise Sales Register",
"link_type": "Report",
"onboard": 0,
@@ -255,6 +282,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Sales Order Analysis",
+ "link_count": 0,
"link_to": "Sales Order Analysis",
"link_type": "Report",
"onboard": 0,
@@ -265,6 +293,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Delivered Items To Be Billed",
+ "link_count": 0,
"link_to": "Delivered Items To Be Billed",
"link_type": "Report",
"onboard": 0,
@@ -274,6 +303,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Accounts Payable",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -282,6 +312,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Purchase Invoice",
+ "link_count": 0,
"link_to": "Purchase Invoice",
"link_type": "DocType",
"onboard": 1,
@@ -292,6 +323,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Supplier",
+ "link_count": 0,
"link_to": "Supplier",
"link_type": "DocType",
"onboard": 1,
@@ -302,6 +334,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Payment Entry",
+ "link_count": 0,
"link_to": "Payment Entry",
"link_type": "DocType",
"onboard": 0,
@@ -312,6 +345,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Accounts Payable",
+ "link_count": 0,
"link_to": "Accounts Payable",
"link_type": "Report",
"onboard": 0,
@@ -322,6 +356,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Accounts Payable Summary",
+ "link_count": 0,
"link_to": "Accounts Payable Summary",
"link_type": "Report",
"onboard": 0,
@@ -332,6 +367,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Purchase Register",
+ "link_count": 0,
"link_to": "Purchase Register",
"link_type": "Report",
"onboard": 0,
@@ -342,6 +378,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Item-wise Purchase Register",
+ "link_count": 0,
"link_to": "Item-wise Purchase Register",
"link_type": "Report",
"onboard": 0,
@@ -352,6 +389,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Purchase Order Analysis",
+ "link_count": 0,
"link_to": "Purchase Order Analysis",
"link_type": "Report",
"onboard": 0,
@@ -362,6 +400,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Received Items To Be Billed",
+ "link_count": 0,
"link_to": "Received Items To Be Billed",
"link_type": "Report",
"onboard": 0,
@@ -371,6 +410,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -379,6 +419,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Trial Balance for Party",
+ "link_count": 0,
"link_to": "Trial Balance for Party",
"link_type": "Report",
"onboard": 0,
@@ -389,6 +430,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Payment Period Based On Invoice Date",
+ "link_count": 0,
"link_to": "Payment Period Based On Invoice Date",
"link_type": "Report",
"onboard": 0,
@@ -399,6 +441,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Sales Partners Commission",
+ "link_count": 0,
"link_to": "Sales Partners Commission",
"link_type": "Report",
"onboard": 0,
@@ -409,6 +452,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Customer Credit Balance",
+ "link_count": 0,
"link_to": "Customer Credit Balance",
"link_type": "Report",
"onboard": 0,
@@ -419,6 +463,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Sales Payment Summary",
+ "link_count": 0,
"link_to": "Sales Payment Summary",
"link_type": "Report",
"onboard": 0,
@@ -429,6 +474,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Address And Contacts",
+ "link_count": 0,
"link_to": "Address And Contacts",
"link_type": "Report",
"onboard": 0,
@@ -439,6 +485,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Tax Detail",
+ "link_count": 0,
"link_to": "Tax Detail",
"link_type": "Report",
"onboard": 0,
@@ -449,6 +496,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "DATEV Export",
+ "link_count": 0,
"link_to": "DATEV",
"link_type": "Report",
"onboard": 0,
@@ -460,6 +508,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "UAE VAT 201",
+ "link_count": 0,
"link_to": "UAE VAT 201",
"link_type": "Report",
"onboard": 0,
@@ -470,6 +519,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Financial Statements",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -478,6 +528,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Trial Balance",
+ "link_count": 0,
"link_to": "Trial Balance",
"link_type": "Report",
"onboard": 0,
@@ -488,6 +539,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Profit and Loss Statement",
+ "link_count": 0,
"link_to": "Profit and Loss Statement",
"link_type": "Report",
"onboard": 0,
@@ -498,6 +550,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Balance Sheet",
+ "link_count": 0,
"link_to": "Balance Sheet",
"link_type": "Report",
"onboard": 0,
@@ -508,6 +561,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Cash Flow",
+ "link_count": 0,
"link_to": "Cash Flow",
"link_type": "Report",
"onboard": 0,
@@ -518,6 +572,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Consolidated Financial Statement",
+ "link_count": 0,
"link_to": "Consolidated Financial Statement",
"link_type": "Report",
"onboard": 0,
@@ -527,6 +582,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Multi Currency",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -535,6 +591,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Currency",
+ "link_count": 0,
"link_to": "Currency",
"link_type": "DocType",
"onboard": 0,
@@ -545,6 +602,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Currency Exchange",
+ "link_count": 0,
"link_to": "Currency Exchange",
"link_type": "DocType",
"onboard": 0,
@@ -555,6 +613,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Exchange Rate Revaluation",
+ "link_count": 0,
"link_to": "Exchange Rate Revaluation",
"link_type": "DocType",
"onboard": 0,
@@ -564,6 +623,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Settings",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -572,6 +632,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Payment Gateway Account",
+ "link_count": 0,
"link_to": "Payment Gateway Account",
"link_type": "DocType",
"onboard": 0,
@@ -582,6 +643,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Terms and Conditions Template",
+ "link_count": 0,
"link_to": "Terms and Conditions",
"link_type": "DocType",
"onboard": 0,
@@ -592,6 +654,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Mode of Payment",
+ "link_count": 0,
"link_to": "Mode of Payment",
"link_type": "DocType",
"onboard": 0,
@@ -601,6 +664,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Bank Statement",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -609,6 +673,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Bank",
+ "link_count": 0,
"link_to": "Bank",
"link_type": "DocType",
"onboard": 0,
@@ -619,6 +684,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Bank Account",
+ "link_count": 0,
"link_to": "Bank Account",
"link_type": "DocType",
"onboard": 0,
@@ -629,6 +695,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Bank Clearance",
+ "link_count": 0,
"link_to": "Bank Clearance",
"link_type": "DocType",
"onboard": 0,
@@ -639,6 +706,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Bank Reconciliation Tool",
+ "link_count": 0,
"link_to": "Bank Reconciliation Tool",
"link_type": "DocType",
"onboard": 0,
@@ -649,6 +717,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Bank Reconciliation Statement",
+ "link_count": 0,
"link_to": "Bank Reconciliation Statement",
"link_type": "Report",
"onboard": 0,
@@ -658,6 +727,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Subscription Management",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -666,6 +736,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Subscription Plan",
+ "link_count": 0,
"link_to": "Subscription Plan",
"link_type": "DocType",
"onboard": 0,
@@ -676,6 +747,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Subscription",
+ "link_count": 0,
"link_to": "Subscription",
"link_type": "DocType",
"onboard": 0,
@@ -686,6 +758,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Subscription Settings",
+ "link_count": 0,
"link_to": "Subscription Settings",
"link_type": "DocType",
"onboard": 0,
@@ -695,6 +768,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Goods and Services Tax (GST India)",
+ "link_count": 0,
"onboard": 0,
"only_for": "India",
"type": "Card Break"
@@ -704,6 +778,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "GST Settings",
+ "link_count": 0,
"link_to": "GST Settings",
"link_type": "DocType",
"onboard": 0,
@@ -715,6 +790,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "GST HSN Code",
+ "link_count": 0,
"link_to": "GST HSN Code",
"link_type": "DocType",
"onboard": 0,
@@ -726,6 +802,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "GSTR-1",
+ "link_count": 0,
"link_to": "GSTR-1",
"link_type": "Report",
"onboard": 0,
@@ -737,6 +814,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "GSTR-2",
+ "link_count": 0,
"link_to": "GSTR-2",
"link_type": "Report",
"onboard": 0,
@@ -748,6 +826,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "GSTR 3B Report",
+ "link_count": 0,
"link_to": "GSTR 3B Report",
"link_type": "DocType",
"onboard": 0,
@@ -759,6 +838,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "GST Sales Register",
+ "link_count": 0,
"link_to": "GST Sales Register",
"link_type": "Report",
"onboard": 0,
@@ -770,6 +850,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "GST Purchase Register",
+ "link_count": 0,
"link_to": "GST Purchase Register",
"link_type": "Report",
"onboard": 0,
@@ -781,6 +862,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "GST Itemised Sales Register",
+ "link_count": 0,
"link_to": "GST Itemised Sales Register",
"link_type": "Report",
"onboard": 0,
@@ -792,6 +874,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "GST Itemised Purchase Register",
+ "link_count": 0,
"link_to": "GST Itemised Purchase Register",
"link_type": "Report",
"onboard": 0,
@@ -803,6 +886,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "C-Form",
+ "link_count": 0,
"link_to": "C-Form",
"link_type": "DocType",
"onboard": 0,
@@ -814,6 +898,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Lower Deduction Certificate",
+ "link_count": 0,
"link_to": "Lower Deduction Certificate",
"link_type": "DocType",
"onboard": 0,
@@ -824,6 +909,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Share Management",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -832,6 +918,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Shareholder",
+ "link_count": 0,
"link_to": "Shareholder",
"link_type": "DocType",
"onboard": 0,
@@ -842,6 +929,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Share Transfer",
+ "link_count": 0,
"link_to": "Share Transfer",
"link_type": "DocType",
"onboard": 0,
@@ -852,6 +940,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Share Ledger",
+ "link_count": 0,
"link_to": "Share Ledger",
"link_type": "Report",
"onboard": 0,
@@ -862,6 +951,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Share Balance",
+ "link_count": 0,
"link_to": "Share Balance",
"link_type": "Report",
"onboard": 0,
@@ -871,6 +961,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Cost Center and Budgeting",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -879,6 +970,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Chart of Cost Centers",
+ "link_count": 0,
"link_to": "Cost Center",
"link_type": "DocType",
"onboard": 0,
@@ -889,6 +981,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Budget",
+ "link_count": 0,
"link_to": "Budget",
"link_type": "DocType",
"onboard": 0,
@@ -899,6 +992,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Accounting Dimension",
+ "link_count": 0,
"link_to": "Accounting Dimension",
"link_type": "DocType",
"onboard": 0,
@@ -909,6 +1003,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Budget Variance Report",
+ "link_count": 0,
"link_to": "Budget Variance Report",
"link_type": "Report",
"onboard": 0,
@@ -919,6 +1014,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Monthly Distribution",
+ "link_count": 0,
"link_to": "Monthly Distribution",
"link_type": "DocType",
"onboard": 0,
@@ -928,6 +1024,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Opening and Closing",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -936,6 +1033,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Opening Invoice Creation Tool",
+ "link_count": 0,
"link_to": "Opening Invoice Creation Tool",
"link_type": "DocType",
"onboard": 0,
@@ -946,6 +1044,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Chart of Accounts Importer",
+ "link_count": 0,
"link_to": "Chart of Accounts Importer",
"link_type": "DocType",
"onboard": 0,
@@ -956,6 +1055,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Period Closing Voucher",
+ "link_count": 0,
"link_to": "Period Closing Voucher",
"link_type": "DocType",
"onboard": 0,
@@ -965,6 +1065,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Taxes",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -973,6 +1074,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Sales Taxes and Charges Template",
+ "link_count": 0,
"link_to": "Sales Taxes and Charges Template",
"link_type": "DocType",
"onboard": 0,
@@ -983,6 +1085,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Purchase Taxes and Charges Template",
+ "link_count": 0,
"link_to": "Purchase Taxes and Charges Template",
"link_type": "DocType",
"onboard": 0,
@@ -993,6 +1096,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item Tax Template",
+ "link_count": 0,
"link_to": "Item Tax Template",
"link_type": "DocType",
"onboard": 0,
@@ -1003,6 +1107,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Tax Category",
+ "link_count": 0,
"link_to": "Tax Category",
"link_type": "DocType",
"onboard": 0,
@@ -1013,6 +1118,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Tax Rule",
+ "link_count": 0,
"link_to": "Tax Rule",
"link_type": "DocType",
"onboard": 0,
@@ -1023,6 +1129,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Tax Withholding Category",
+ "link_count": 0,
"link_to": "Tax Withholding Category",
"link_type": "DocType",
"onboard": 0,
@@ -1032,6 +1139,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Profitability",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -1040,6 +1148,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Gross Profit",
+ "link_count": 0,
"link_to": "Gross Profit",
"link_type": "Report",
"onboard": 0,
@@ -1050,6 +1159,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Profitability Analysis",
+ "link_count": 0,
"link_to": "Profitability Analysis",
"link_type": "Report",
"onboard": 0,
@@ -1060,6 +1170,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Sales Invoice Trends",
+ "link_count": 0,
"link_to": "Sales Invoice Trends",
"link_type": "Report",
"onboard": 0,
@@ -1070,20 +1181,26 @@
"hidden": 0,
"is_query_report": 1,
"label": "Purchase Invoice Trends",
+ "link_count": 0,
"link_to": "Purchase Invoice Trends",
"link_type": "Report",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2021-06-10 03:17:31.427945",
+ "modified": "2021-08-05 12:15:52.872470",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting",
"onboarding": "Accounts",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 2,
"shortcuts": [
{
"label": "Chart of Accounts",
@@ -1130,5 +1247,6 @@
"link_to": "Accounts",
"type": "Dashboard"
}
- ]
+ ],
+ "title": "Accounting"
}
\ No newline at end of file
diff --git a/erpnext/agriculture/workspace/agriculture/agriculture.json b/erpnext/agriculture/workspace/agriculture/agriculture.json
index 2cc2524..633777e 100644
--- a/erpnext/agriculture/workspace/agriculture/agriculture.json
+++ b/erpnext/agriculture/workspace/agriculture/agriculture.json
@@ -1,22 +1,27 @@
{
- "category": "Domains",
+ "category": "",
"charts": [],
+ "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Crops & Lands\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Analytics\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Diseases & Fertilizers\", \"col\": 4}}]",
"creation": "2020-03-02 17:23:34.339274",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "agriculture",
"idx": 0,
- "is_standard": 1,
+ "is_default": 0,
+ "is_standard": 0,
"label": "Agriculture",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Crops & Lands",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -25,6 +30,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Crop",
+ "link_count": 0,
"link_to": "Crop",
"link_type": "DocType",
"onboard": 1,
@@ -35,6 +41,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Crop Cycle",
+ "link_count": 0,
"link_to": "Crop Cycle",
"link_type": "DocType",
"onboard": 1,
@@ -45,6 +52,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Location",
+ "link_count": 0,
"link_to": "Location",
"link_type": "DocType",
"onboard": 1,
@@ -54,6 +62,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Analytics",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -62,6 +71,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Plant Analysis",
+ "link_count": 0,
"link_to": "Plant Analysis",
"link_type": "DocType",
"onboard": 0,
@@ -72,6 +82,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Soil Analysis",
+ "link_count": 0,
"link_to": "Soil Analysis",
"link_type": "DocType",
"onboard": 0,
@@ -82,6 +93,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Water Analysis",
+ "link_count": 0,
"link_to": "Water Analysis",
"link_type": "DocType",
"onboard": 0,
@@ -92,6 +104,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Soil Texture",
+ "link_count": 0,
"link_to": "Soil Texture",
"link_type": "DocType",
"onboard": 0,
@@ -102,6 +115,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Weather",
+ "link_count": 0,
"link_to": "Weather",
"link_type": "DocType",
"onboard": 0,
@@ -112,6 +126,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Agriculture Analysis Criteria",
+ "link_count": 0,
"link_to": "Agriculture Analysis Criteria",
"link_type": "DocType",
"onboard": 0,
@@ -121,6 +136,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Diseases & Fertilizers",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -129,6 +145,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Disease",
+ "link_count": 0,
"link_to": "Disease",
"link_type": "DocType",
"onboard": 1,
@@ -139,19 +156,26 @@
"hidden": 0,
"is_query_report": 0,
"label": "Fertilizer",
+ "link_count": 0,
"link_to": "Fertilizer",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
}
],
- "modified": "2020-12-01 13:38:38.477493",
+ "modified": "2021-08-05 12:15:54.595197",
"modified_by": "Administrator",
"module": "Agriculture",
"name": "Agriculture",
+ "onboarding": "",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
"restrict_to_domain": "Agriculture",
- "shortcuts": []
+ "roles": [],
+ "sequence_id": 3,
+ "shortcuts": [],
+ "title": "Agriculture"
}
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 66f0bdc..b7ca693 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -56,12 +56,12 @@
if self.is_existing_asset and self.purchase_invoice:
frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name))
- def prepare_depreciation_data(self):
+ def prepare_depreciation_data(self, date_of_sale=None):
if self.calculate_depreciation:
self.value_after_depreciation = 0
self.set_depreciation_rate()
- self.make_depreciation_schedule()
- self.set_accumulated_depreciation()
+ self.make_depreciation_schedule(date_of_sale)
+ self.set_accumulated_depreciation(date_of_sale)
else:
self.finance_books = []
self.value_after_depreciation = (flt(self.gross_purchase_amount) -
@@ -167,7 +167,7 @@
d.rate_of_depreciation = flt(self.get_depreciation_rate(d, on_validate=True),
d.precision("rate_of_depreciation"))
- def make_depreciation_schedule(self):
+ def make_depreciation_schedule(self, date_of_sale):
if 'Manual' not in [d.depreciation_method for d in self.finance_books] and not self.schedules:
self.schedules = []
@@ -212,6 +212,21 @@
# so monthly schedule date is calculated by removing 11 months from it
monthly_schedule_date = add_months(schedule_date, - d.frequency_of_depreciation + 1)
+ # if asset is being sold
+ if date_of_sale:
+ from_date = self.get_from_date(d.finance_book)
+ depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount,
+ from_date, date_of_sale)
+
+ self.append("schedules", {
+ "schedule_date": date_of_sale,
+ "depreciation_amount": depreciation_amount,
+ "depreciation_method": d.depreciation_method,
+ "finance_book": d.finance_book,
+ "finance_book_id": d.idx
+ })
+ break
+
# For first row
if has_pro_rata and n==0:
depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount,
@@ -303,6 +318,21 @@
break
return start
+ def get_from_date(self, finance_book):
+ if not self.get('schedules'):
+ return self.available_for_use_date
+
+ if len(self.finance_books) == 1:
+ return self.schedules[-1].schedule_date
+
+ from_date = ""
+ for schedule in self.get('schedules'):
+ if schedule.finance_book == finance_book:
+ from_date = schedule.schedule_date
+
+ if from_date:
+ return from_date
+ return self.available_for_use_date
# if it returns True, depreciation_amount will not be equal for the first and last rows
def check_is_pro_rata(self, row):
@@ -357,7 +387,7 @@
frappe.throw(_("Depreciation Row {0}: Next Depreciation Date cannot be before Available-for-use Date")
.format(row.idx))
- def set_accumulated_depreciation(self, ignore_booked_entry = False):
+ def set_accumulated_depreciation(self, date_of_sale=None, ignore_booked_entry = False):
straight_line_idx = [d.idx for d in self.get("schedules") if d.depreciation_method == 'Straight Line']
finance_books = []
@@ -365,7 +395,7 @@
if ignore_booked_entry and d.journal_entry:
continue
- if d.finance_book_id not in finance_books:
+ if int(d.finance_book_id) not in finance_books:
accumulated_depreciation = flt(self.opening_accumulated_depreciation)
value_after_depreciation = flt(self.get_value_after_depreciation(d.finance_book_id))
finance_books.append(int(d.finance_book_id))
@@ -374,7 +404,7 @@
value_after_depreciation -= flt(depreciation_amount)
# for the last row, if depreciation method = Straight Line
- if straight_line_idx and i == max(straight_line_idx) - 1:
+ if straight_line_idx and i == max(straight_line_idx) - 1 and not date_of_sale:
book = self.get('finance_books')[cint(d.finance_book_id) - 1]
depreciation_amount += flt(value_after_depreciation -
flt(book.expected_value_after_useful_life), d.precision("depreciation_amount"))
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 59fbe3b..e23a715 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -639,7 +639,7 @@
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
asset = frappe.get_doc('Asset', asset_name)
asset.calculate_depreciation = 1
- asset.available_for_use_date = '2030-06-12'
+ asset.available_for_use_date = '2030-07-12'
asset.purchase_date = '2030-01-01'
asset.append("finance_books", {
"expected_value_after_useful_life": 1000,
@@ -653,10 +653,10 @@
self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
expected_schedules = [
- ["2030-12-31", 1106.85, 1106.85],
- ["2031-12-31", 3446.58, 4553.43],
- ["2032-12-31", 1723.29, 6276.72],
- ["2033-06-12", 723.28, 7000.00]
+ ["2030-12-31", 942.47, 942.47],
+ ["2031-12-31", 3528.77, 4471.24],
+ ["2032-12-31", 1764.38, 6235.62],
+ ["2033-07-12", 764.38, 7000.00]
]
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
diff --git a/erpnext/assets/doctype/asset_movement/test_asset_movement.py b/erpnext/assets/doctype/asset_movement/test_asset_movement.py
index cddee5f..2b2d2b4 100644
--- a/erpnext/assets/doctype/asset_movement/test_asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/test_asset_movement.py
@@ -15,6 +15,7 @@
class TestAssetMovement(unittest.TestCase):
def setUp(self):
+ frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "CWIP Account - _TC")
create_asset_data()
make_location()
@@ -45,12 +46,12 @@
'location_name': 'Test Location 2'
}).insert()
- movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
+ movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}],
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
- movement2 = create_asset_movement(purpose = 'Transfer', company = asset.company,
+ create_asset_movement(purpose = 'Transfer', company = asset.company,
assets = [{ 'asset': asset.name , 'source_location': 'Test Location 2', 'target_location': 'Test Location'}],
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
@@ -59,18 +60,18 @@
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
employee = make_employee("testassetmovemp@example.com", company="_Test Company")
- movement3 = create_asset_movement(purpose = 'Issue', company = asset.company,
+ create_asset_movement(purpose = 'Issue', company = asset.company,
assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'to_employee': employee}],
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
-
+
# after issuing asset should belong to an employee not at a location
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), None)
self.assertEqual(frappe.db.get_value("Asset", asset.name, "custodian"), employee)
-
+
def test_last_movement_cancellation(self):
pr = make_purchase_receipt(item_code="Macbook Pro",
qty=1, rate=100000.0, location="Test Location")
-
+
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
asset = frappe.get_doc('Asset', asset_name)
asset.calculate_depreciation = 1
@@ -85,17 +86,17 @@
})
if asset.docstatus == 0:
asset.submit()
-
+
if not frappe.db.exists("Location", "Test Location 2"):
frappe.get_doc({
'doctype': 'Location',
'location_name': 'Test Location 2'
}).insert()
-
+
movement = frappe.get_doc({'doctype': 'Asset Movement', 'reference_name': pr.name })
self.assertRaises(frappe.ValidationError, movement.cancel)
- movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
+ movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}],
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
diff --git a/erpnext/assets/workspace/assets/assets.json b/erpnext/assets/workspace/assets/assets.json
index c401581..dfbf1a3 100644
--- a/erpnext/assets/workspace/assets/assets.json
+++ b/erpnext/assets/workspace/assets/assets.json
@@ -1,27 +1,32 @@
{
- "category": "Modules",
+ "category": "",
"charts": [
{
"chart_name": "Asset Value Analytics",
"label": "Asset Value Analytics"
}
],
+ "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Assets\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Asset Value Analytics\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Asset\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Asset Category\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Fixed Asset Register\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Assets\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Maintenance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
"creation": "2020-03-02 15:43:27.634865",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "assets",
"idx": 0,
- "is_standard": 1,
+ "is_default": 0,
+ "is_standard": 0,
"label": "Assets",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Assets",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -30,6 +35,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Asset",
+ "link_count": 0,
"link_to": "Asset",
"link_type": "DocType",
"onboard": 1,
@@ -40,6 +46,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Location",
+ "link_count": 0,
"link_to": "Location",
"link_type": "DocType",
"onboard": 1,
@@ -50,6 +57,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Asset Category",
+ "link_count": 0,
"link_to": "Asset Category",
"link_type": "DocType",
"onboard": 1,
@@ -60,6 +68,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Asset Movement",
+ "link_count": 0,
"link_to": "Asset Movement",
"link_type": "DocType",
"onboard": 0,
@@ -69,6 +78,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Maintenance",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -77,6 +87,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Asset Maintenance Team",
+ "link_count": 0,
"link_to": "Asset Maintenance Team",
"link_type": "DocType",
"onboard": 1,
@@ -87,6 +98,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Asset Maintenance",
+ "link_count": 0,
"link_to": "Asset Maintenance",
"link_type": "DocType",
"onboard": 1,
@@ -97,6 +109,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Asset Maintenance Log",
+ "link_count": 0,
"link_to": "Asset Maintenance Log",
"link_type": "DocType",
"onboard": 0,
@@ -107,6 +120,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Asset Value Adjustment",
+ "link_count": 0,
"link_to": "Asset Value Adjustment",
"link_type": "DocType",
"onboard": 0,
@@ -117,6 +131,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Asset Repair",
+ "link_count": 0,
"link_to": "Asset Repair",
"link_type": "DocType",
"onboard": 0,
@@ -126,6 +141,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -134,6 +150,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Asset Depreciation Ledger",
+ "link_count": 0,
"link_to": "Asset Depreciation Ledger",
"link_type": "Report",
"onboard": 0,
@@ -144,6 +161,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Asset Depreciations and Balances",
+ "link_count": 0,
"link_to": "Asset Depreciations and Balances",
"link_type": "Report",
"onboard": 0,
@@ -154,20 +172,26 @@
"hidden": 0,
"is_query_report": 0,
"label": "Asset Maintenance",
+ "link_count": 0,
"link_to": "Asset Maintenance",
"link_type": "Report",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2020-12-01 13:38:37.977119",
+ "modified": "2021-08-05 12:15:54.839452",
"modified_by": "Administrator",
"module": "Assets",
"name": "Assets",
"onboarding": "Assets",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 4,
"shortcuts": [
{
"label": "Asset",
@@ -189,5 +213,6 @@
"link_to": "Asset",
"type": "Dashboard"
}
- ]
+ ],
+ "title": "Assets"
}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index eaa502f..a0b1e07 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -447,10 +447,11 @@
target.flags.ignore_permissions = ignore_permissions
set_missing_values(source, target)
#Get the advance paid Journal Entries in Purchase Invoice Advance
-
if target.get("allocate_advances_automatically"):
target.set_advances()
+ target.set_payment_schedule()
+
def update_item(obj, target, source_parent):
target.amount = flt(obj.amount) - flt(obj.billed_amt)
target.base_amount = target.amount * flt(source_parent.conversion_rate)
@@ -470,6 +471,7 @@
"party_account_currency": "party_account_currency",
"supplier_warehouse":"supplier_warehouse"
},
+ "field_no_map" : ["payment_terms_template"],
"validation": {
"docstatus": ["=", 1],
}
@@ -489,12 +491,6 @@
},
}
- if frappe.get_single("Accounts Settings").automatically_fetch_payment_terms == 1:
- fields["Payment Schedule"] = {
- "doctype": "Payment Schedule",
- "add_if_empty": True
- }
-
doc = get_mapped_doc("Purchase Order", source_name, fields,
target_doc, postprocess, ignore_permissions=ignore_permissions)
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 8563b97..d668c76 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -484,6 +484,9 @@
def test_make_purchase_invoice_with_terms(self):
+ from erpnext.selling.doctype.sales_order.test_sales_order import automatically_fetch_payment_terms, compare_payment_schedules
+
+ automatically_fetch_payment_terms()
po = create_purchase_order(do_not_save=True)
self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name)
@@ -509,6 +512,7 @@
self.assertEqual(getdate(pi.payment_schedule[0].due_date), getdate(po.transaction_date))
self.assertEqual(pi.payment_schedule[1].payment_amount, 2500.0)
self.assertEqual(getdate(pi.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
+ automatically_fetch_payment_terms(enable=0)
def test_subcontracting(self):
po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
@@ -632,14 +636,18 @@
else:
raise Exception
- def test_terms_does_not_copy(self):
- po = create_purchase_order()
+ def test_terms_are_not_copied_if_automatically_fetch_payment_terms_is_unchecked(self):
+ po = create_purchase_order(do_not_save=1)
+ po.payment_terms_template = '_Test Payment Term Template'
+ po.save()
+ po.submit()
- self.assertTrue(po.get('payment_schedule'))
-
+ frappe.db.set_value('Company', '_Test Company', 'payment_terms', '_Test Payment Term Template 1')
pi = make_pi_from_po(po.name)
+ pi.save()
- self.assertFalse(pi.get('payment_schedule'))
+ self.assertEqual(pi.get('payment_terms_template'), '_Test Payment Term Template 1')
+ frappe.db.set_value('Company', '_Test Company', 'payment_terms', '')
def test_terms_copied(self):
po = create_purchase_order(do_not_save=1)
@@ -968,8 +976,27 @@
# To test if the PO does NOT have a Blanket Order
self.assertEqual(po_doc.items[0].blanket_order, None)
+ def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
+ from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+ from erpnext.selling.doctype.sales_order.test_sales_order import automatically_fetch_payment_terms, compare_payment_schedules
+ automatically_fetch_payment_terms()
+ po = create_purchase_order(qty=10, rate=100, do_not_save=1)
+ create_payment_terms_template()
+ po.payment_terms_template = 'Test Receivable Template'
+ po.submit()
+
+ pi = make_purchase_invoice(qty=10, rate=100, do_not_save=1)
+ pi.items[0].purchase_order = po.name
+ pi.items[0].po_detail = po.items[0].name
+ pi.insert()
+
+ # self.assertEqual(po.payment_terms_template, pi.payment_terms_template)
+ compare_payment_schedules(self, po, pi)
+
+ automatically_fetch_payment_terms(enable=0)
def make_pr_against_po(po, received_qty=0):
pr = make_purchase_receipt(po)
diff --git a/erpnext/buying/workspace/buying/buying.json b/erpnext/buying/workspace/buying/buying.json
index 6c9c0f3..6c91e81 100644
--- a/erpnext/buying/workspace/buying/buying.json
+++ b/erpnext/buying/workspace/buying/buying.json
@@ -1,6 +1,6 @@
{
"cards_label": "",
- "category": "Modules",
+ "category": "",
"charts": [
{
"chart_name": "Purchase Order Trends",
@@ -8,22 +8,27 @@
}
],
"charts_label": "",
+ "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Buying\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Purchase Order Trends\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Material Request\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Order\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Analytics\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Order Analysis\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Buying\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Items & Pricing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Supplier\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Supplier Scorecard\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Regional\", \"col\": 4}}]",
"creation": "2020-01-28 11:50:26.195467",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "buying",
"idx": 0,
- "is_standard": 1,
+ "is_default": 0,
+ "is_standard": 0,
"label": "Buying",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Buying",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -32,6 +37,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Material Request",
+ "link_count": 0,
"link_to": "Material Request",
"link_type": "DocType",
"onboard": 1,
@@ -42,6 +48,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Purchase Order",
+ "link_count": 0,
"link_to": "Purchase Order",
"link_type": "DocType",
"onboard": 1,
@@ -52,6 +59,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Purchase Invoice",
+ "link_count": 0,
"link_to": "Purchase Invoice",
"link_type": "DocType",
"onboard": 1,
@@ -62,6 +70,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Request for Quotation",
+ "link_count": 0,
"link_to": "Request for Quotation",
"link_type": "DocType",
"onboard": 1,
@@ -72,6 +81,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Supplier Quotation",
+ "link_count": 0,
"link_to": "Supplier Quotation",
"link_type": "DocType",
"onboard": 0,
@@ -81,6 +91,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Items & Pricing",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -89,6 +100,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item",
+ "link_count": 0,
"link_to": "Item",
"link_type": "DocType",
"onboard": 1,
@@ -99,6 +111,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item Price",
+ "link_count": 0,
"link_to": "Item Price",
"link_type": "DocType",
"onboard": 1,
@@ -109,6 +122,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Price List",
+ "link_count": 0,
"link_to": "Price List",
"link_type": "DocType",
"onboard": 1,
@@ -119,6 +133,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Product Bundle",
+ "link_count": 0,
"link_to": "Product Bundle",
"link_type": "DocType",
"onboard": 0,
@@ -129,6 +144,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item Group",
+ "link_count": 0,
"link_to": "Item Group",
"link_type": "DocType",
"onboard": 0,
@@ -139,6 +155,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Promotional Scheme",
+ "link_count": 0,
"link_to": "Promotional Scheme",
"link_type": "DocType",
"onboard": 0,
@@ -149,6 +166,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Pricing Rule",
+ "link_count": 0,
"link_to": "Pricing Rule",
"link_type": "DocType",
"onboard": 0,
@@ -158,6 +176,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Settings",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -166,6 +185,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Buying Settings",
+ "link_count": 0,
"link_to": "Buying Settings",
"link_type": "DocType",
"onboard": 0,
@@ -176,6 +196,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Purchase Taxes and Charges Template",
+ "link_count": 0,
"link_to": "Purchase Taxes and Charges Template",
"link_type": "DocType",
"onboard": 0,
@@ -186,6 +207,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Terms and Conditions Template",
+ "link_count": 0,
"link_to": "Terms and Conditions",
"link_type": "DocType",
"onboard": 0,
@@ -195,6 +217,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Supplier",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -203,6 +226,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Supplier",
+ "link_count": 0,
"link_to": "Supplier",
"link_type": "DocType",
"onboard": 1,
@@ -213,6 +237,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Supplier Group",
+ "link_count": 0,
"link_to": "Supplier Group",
"link_type": "DocType",
"onboard": 0,
@@ -223,6 +248,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Contact",
+ "link_count": 0,
"link_to": "Contact",
"link_type": "DocType",
"onboard": 0,
@@ -233,6 +259,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Address",
+ "link_count": 0,
"link_to": "Address",
"link_type": "DocType",
"onboard": 0,
@@ -242,6 +269,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Supplier Scorecard",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -250,6 +278,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Supplier Scorecard",
+ "link_count": 0,
"link_to": "Supplier Scorecard",
"link_type": "DocType",
"onboard": 0,
@@ -260,6 +289,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Supplier Scorecard Variable",
+ "link_count": 0,
"link_to": "Supplier Scorecard Variable",
"link_type": "DocType",
"onboard": 0,
@@ -270,6 +300,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Supplier Scorecard Criteria",
+ "link_count": 0,
"link_to": "Supplier Scorecard Criteria",
"link_type": "DocType",
"onboard": 0,
@@ -280,6 +311,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Supplier Scorecard Standing",
+ "link_count": 0,
"link_to": "Supplier Scorecard Standing",
"link_type": "DocType",
"onboard": 0,
@@ -289,6 +321,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Key Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -297,6 +330,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Purchase Analytics",
+ "link_count": 0,
"link_to": "Purchase Analytics",
"link_type": "Report",
"onboard": 1,
@@ -307,6 +341,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Purchase Order Analysis",
+ "link_count": 0,
"link_to": "Purchase Order Analysis",
"link_type": "Report",
"onboard": 1,
@@ -317,6 +352,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Supplier-Wise Sales Analytics",
+ "link_count": 0,
"link_to": "Supplier-Wise Sales Analytics",
"link_type": "Report",
"onboard": 1,
@@ -327,6 +363,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Items to Order and Receive",
+ "link_count": 0,
"link_to": "Requested Items to Order and Receive",
"link_type": "Report",
"onboard": 1,
@@ -337,6 +374,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Purchase Order Trends",
+ "link_count": 0,
"link_to": "Purchase Order Trends",
"link_type": "Report",
"onboard": 1,
@@ -347,6 +385,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Procurement Tracker",
+ "link_count": 0,
"link_to": "Procurement Tracker",
"link_type": "Report",
"onboard": 1,
@@ -356,6 +395,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Other Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -364,6 +404,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Items To Be Requested",
+ "link_count": 0,
"link_to": "Items To Be Requested",
"link_type": "Report",
"onboard": 1,
@@ -374,6 +415,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Item-wise Purchase History",
+ "link_count": 0,
"link_to": "Item-wise Purchase History",
"link_type": "Report",
"onboard": 1,
@@ -384,6 +426,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Purchase Receipt Trends",
+ "link_count": 0,
"link_to": "Purchase Receipt Trends",
"link_type": "Report",
"onboard": 0,
@@ -394,6 +437,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Purchase Invoice Trends",
+ "link_count": 0,
"link_to": "Purchase Invoice Trends",
"link_type": "Report",
"onboard": 0,
@@ -404,6 +448,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Subcontracted Raw Materials To Be Transferred",
+ "link_count": 0,
"link_to": "Subcontracted Raw Materials To Be Transferred",
"link_type": "Report",
"onboard": 0,
@@ -414,6 +459,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Subcontracted Item To Be Received",
+ "link_count": 0,
"link_to": "Subcontracted Item To Be Received",
"link_type": "Report",
"onboard": 0,
@@ -424,6 +470,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Supplier Quotation Comparison",
+ "link_count": 0,
"link_to": "Supplier Quotation Comparison",
"link_type": "Report",
"onboard": 1,
@@ -434,6 +481,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Material Requests for which Supplier Quotations are not created",
+ "link_count": 0,
"link_to": "Material Requests for which Supplier Quotations are not created",
"link_type": "Report",
"onboard": 0,
@@ -444,6 +492,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Supplier Addresses And Contacts",
+ "link_count": 0,
"link_to": "Address And Contacts",
"link_type": "Report",
"onboard": 0,
@@ -453,6 +502,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Regional",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -461,20 +511,26 @@
"hidden": 0,
"is_query_report": 0,
"label": "Import Supplier Invoice",
+ "link_count": 0,
"link_to": "Import Supplier Invoice",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2020-12-01 13:38:38.615167",
+ "modified": "2021-08-05 12:15:56.218427",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying",
"onboarding": "Buying",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 6,
"shortcuts": [
{
"color": "Green",
@@ -516,5 +572,6 @@
"type": "Dashboard"
}
],
- "shortcuts_label": ""
+ "shortcuts_label": "",
+ "title": "Buying"
}
\ No newline at end of file
diff --git a/erpnext/change_log/v13/v13_8_0.md b/erpnext/change_log/v13/v13_8_0.md
new file mode 100644
index 0000000..98ed95a
--- /dev/null
+++ b/erpnext/change_log/v13/v13_8_0.md
@@ -0,0 +1,39 @@
+# Version 13.8.0 Release Notes
+
+### Features & Enhancements
+- Report to show COGS by item groups ([#26222](https://github.com/frappe/erpnext/pull/26222))
+- Enhancements in TDS ([#26677](https://github.com/frappe/erpnext/pull/26677))
+- API Endpoint to update halted Razorpay subscriptions ([#26564](https://github.com/frappe/erpnext/pull/26564))
+
+### Fixes
+- Incorrect bom name ([#26600](https://github.com/frappe/erpnext/pull/26600))
+- Exchange rate revaluation posting date and precision fixes ([#26651](https://github.com/frappe/erpnext/pull/26651))
+- POS item cart dom updates ([#26460](https://github.com/frappe/erpnext/pull/26460))
+- General Ledger report not working with filter group by ([#26439](https://github.com/frappe/erpnext/pull/26438))
+- Tax calculation for Recurring additional salary ([#24206](https://github.com/frappe/erpnext/pull/24206))
+- Validation check for batch for stock reconciliation type in stock entry ([#26487](https://github.com/frappe/erpnext/pull/26487))
+- Improved UX for additional discount field ([#26502](https://github.com/frappe/erpnext/pull/26502))
+- Add missing cess amount in GSTR-3B report ([#26644](https://github.com/frappe/erpnext/pull/26644))
+- Optimized code for reposting item valuation ([#26431](https://github.com/frappe/erpnext/pull/26431))
+- FG item not fetched in manufacture entry ([#26508](https://github.com/frappe/erpnext/pull/26508))
+- Errors on parallel requests creation of company for India ([#26420](https://github.com/frappe/erpnext/pull/26420))
+- Incorrect valuation rate calculation in gross profit report ([#26558](https://github.com/frappe/erpnext/pull/26558))
+- Empty "against account" in Purchase Receipt GLE ([#26712](https://github.com/frappe/erpnext/pull/26712))
+- Remove cancelled entries from Stock and Account Value comparison report ([#26721](https://github.com/frappe/erpnext/pull/26721))
+- Remove manual permission checking ([#26691](https://github.com/frappe/erpnext/pull/26691))
+- Delete child docs when parent doc is deleted ([#26518](https://github.com/frappe/erpnext/pull/26518))
+- GST Reports timeout issue ([#26646](https://github.com/frappe/erpnext/pull/26646))
+- Parent condition in pricing rules ([#26727](https://github.com/frappe/erpnext/pull/26727))
+- Added Company filters for Loan ([#26294](https://github.com/frappe/erpnext/pull/26294))
+- Incorrect discount amount on amended document ([#26292](https://github.com/frappe/erpnext/pull/26292))
+- Exchange gain loss not set for advances linked with invoices ([#26436](https://github.com/frappe/erpnext/pull/26436))
+- Unallocated amount in Payment Entry after taxes ([#26412](https://github.com/frappe/erpnext/pull/26412))
+- Wrong operation time in Work Order ([#26613](https://github.com/frappe/erpnext/pull/26613))
+- Serial No and Batch validation ([#26614](https://github.com/frappe/erpnext/pull/26614))
+- Gl Entries for exchange gain loss ([#26734](https://github.com/frappe/erpnext/pull/26734))
+- TDS computation summary shows cancelled invoices ([#26485](https://github.com/frappe/erpnext/pull/26485))
+- Price List rate not fetched for return sales invoice fixed ([#26560](https://github.com/frappe/erpnext/pull/26560))
+- Included company in link document type filters for contact ([#26576](https://github.com/frappe/erpnext/pull/26576))
+- Ignore mandatory fields while creating payment reconciliation Journal Entry ([#26643](https://github.com/frappe/erpnext/pull/26643))
+- Unable to download GSTR-1 json ([#26418](https://github.com/frappe/erpnext/pull/26418))
+- Paging buttons not working on item group portal page ([#26498](https://github.com/frappe/erpnext/pull/26498))
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 8199b10..4c79a5c 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -674,19 +674,24 @@
if self.get('doctype') in ['Purchase Invoice', 'Sales Invoice']:
for d in self.get("advances"):
if d.exchange_gain_loss:
- party = self.supplier if self.get('doctype') == 'Purchase Invoice' else self.customer
- party_account = self.credit_to if self.get('doctype') == 'Purchase Invoice' else self.debit_to
- party_type = "Supplier" if self.get('doctype') == 'Purchase Invoice' else "Customer"
+ is_purchase_invoice = self.get('doctype') == 'Purchase Invoice'
+ party = self.supplier if is_purchase_invoice else self.customer
+ party_account = self.credit_to if is_purchase_invoice else self.debit_to
+ party_type = "Supplier" if is_purchase_invoice else "Customer"
gain_loss_account = frappe.db.get_value('Company', self.company, 'exchange_gain_loss_account')
+ if not gain_loss_account:
+ frappe.throw(_("Please set Default Exchange Gain/Loss Account in Company {}")
+ .format(self.get('company')))
account_currency = get_account_currency(gain_loss_account)
if account_currency != self.company_currency:
- frappe.throw(_("Currency for {0} must be {1}").format(d.account, self.company_currency))
+ frappe.throw(_("Currency for {0} must be {1}").format(gain_loss_account, self.company_currency))
# for purchase
dr_or_cr = 'debit' if d.exchange_gain_loss > 0 else 'credit'
- # just reverse for sales?
- dr_or_cr = 'debit' if dr_or_cr == 'credit' else 'credit'
+ if not is_purchase_invoice:
+ # just reverse for sales?
+ dr_or_cr = 'debit' if dr_or_cr == 'credit' else 'credit'
gl_entries.append(
self.get_gl_dict({
@@ -987,9 +992,9 @@
frappe.throw(_("Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings")
.format(item.item_code, item.idx, max_allowed_amt))
- def get_company_default(self, fieldname):
+ def get_company_default(self, fieldname, ignore_validation=False):
from erpnext.accounts.utils import get_company_default
- return get_company_default(self.company, fieldname)
+ return get_company_default(self.company, fieldname, ignore_validation=ignore_validation)
def get_stock_items(self):
stock_items = []
@@ -1174,6 +1179,8 @@
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
base_grand_total = base_grand_total - flt(self.base_write_off_amount)
grand_total = grand_total - flt(self.write_off_amount)
+ po_or_so, doctype, fieldname = self.get_order_details()
+ automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms'))
if self.get("total_advance"):
if party_account_currency == self.company_currency:
@@ -1184,19 +1191,86 @@
base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total"))
if not self.get("payment_schedule"):
- if self.get("payment_terms_template"):
+ if self.doctype in ["Sales Invoice", "Purchase Invoice"] and automatically_fetch_payment_terms \
+ and self.linked_order_has_payment_terms(po_or_so, fieldname, doctype):
+ self.fetch_payment_terms_from_order(po_or_so, doctype)
+ if self.get('payment_terms_template'):
+ self.ignore_default_payment_terms_template = 1
+ elif self.get("payment_terms_template"):
data = get_payment_terms(self.payment_terms_template, posting_date, grand_total, base_grand_total)
for item in data:
self.append("payment_schedule", item)
- else:
+ elif self.doctype not in ["Purchase Receipt"]:
data = dict(due_date=due_date, invoice_portion=100, payment_amount=grand_total, base_payment_amount=base_grand_total)
self.append("payment_schedule", data)
+
+ for d in self.get("payment_schedule"):
+ if d.invoice_portion:
+ d.payment_amount = flt(grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount'))
+ d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('base_payment_amount'))
+ d.outstanding = d.payment_amount
+ elif not d.invoice_portion:
+ d.base_payment_amount = flt(base_grand_total * self.get("conversion_rate"), d.precision('base_payment_amount'))
+
+
+ def get_order_details(self):
+ if self.doctype == "Sales Invoice":
+ po_or_so = self.get('items')[0].get('sales_order')
+ po_or_so_doctype = "Sales Order"
+ po_or_so_doctype_name = "sales_order"
+
else:
- for d in self.get("payment_schedule"):
- if d.invoice_portion:
- d.payment_amount = flt(grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount'))
- d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount'))
- d.outstanding = d.payment_amount
+ po_or_so = self.get('items')[0].get('purchase_order')
+ po_or_so_doctype = "Purchase Order"
+ po_or_so_doctype_name = "purchase_order"
+
+ return po_or_so, po_or_so_doctype, po_or_so_doctype_name
+
+ def linked_order_has_payment_terms(self, po_or_so, fieldname, doctype):
+ if po_or_so and self.all_items_have_same_po_or_so(po_or_so, fieldname):
+ if self.linked_order_has_payment_terms_template(po_or_so, doctype):
+ return True
+ elif self.linked_order_has_payment_schedule(po_or_so):
+ return True
+
+ return False
+
+ def all_items_have_same_po_or_so(self, po_or_so, fieldname):
+ for item in self.get('items'):
+ if item.get(fieldname) != po_or_so:
+ return False
+
+ return True
+
+ def linked_order_has_payment_terms_template(self, po_or_so, doctype):
+ return frappe.get_value(doctype, po_or_so, 'payment_terms_template')
+
+ def linked_order_has_payment_schedule(self, po_or_so):
+ return frappe.get_all('Payment Schedule', filters={'parent': po_or_so})
+
+ def fetch_payment_terms_from_order(self, po_or_so, po_or_so_doctype):
+ """
+ Fetch Payment Terms from Purchase/Sales Order on creating a new Purchase/Sales Invoice.
+ """
+ po_or_so = frappe.get_cached_doc(po_or_so_doctype, po_or_so)
+
+ self.payment_schedule = []
+ self.payment_terms_template = po_or_so.payment_terms_template
+
+ for schedule in po_or_so.payment_schedule:
+ payment_schedule = {
+ 'payment_term': schedule.payment_term,
+ 'due_date': schedule.due_date,
+ 'invoice_portion': schedule.invoice_portion,
+ 'mode_of_payment': schedule.mode_of_payment,
+ 'description': schedule.description
+ }
+
+ if schedule.discount_type == 'Percentage':
+ payment_schedule['discount_type'] = schedule.discount_type
+ payment_schedule['discount'] = schedule.discount
+
+ self.append("payment_schedule", payment_schedule)
def set_due_date(self):
due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date]
@@ -1363,6 +1437,27 @@
tax.rate = None
+def validate_account_head(tax, doc):
+ company = frappe.get_cached_value('Account',
+ tax.account_head, 'company')
+
+ if company != doc.company:
+ frappe.throw(_('Row {0}: Account {1} does not belong to Company {2}')
+ .format(tax.idx, frappe.bold(tax.account_head), frappe.bold(doc.company)), title=_('Invalid Account'))
+
+
+def validate_cost_center(tax, doc):
+ if not tax.cost_center:
+ return
+
+ company = frappe.get_cached_value('Cost Center',
+ tax.cost_center, 'company')
+
+ if company != doc.company:
+ frappe.throw(_('Row {0}: Cost Center {1} does not belong to Company {2}')
+ .format(tax.idx, frappe.bold(tax.cost_center), frappe.bold(doc.company)), title=_('Invalid Cost Center'))
+
+
def validate_inclusive_tax(tax, doc):
def _on_previous_row_error(row_range):
throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, row_range))
@@ -1582,7 +1677,7 @@
if child_item.get("item_tax_template"):
child_item.item_tax_rate = get_item_tax_map(parent_doc.get('company'), child_item.item_tax_template, as_json=True)
-def add_taxes_from_tax_template(child_item, parent_doc):
+def add_taxes_from_tax_template(child_item, parent_doc, db_insert=True):
add_taxes_from_item_tax_template = frappe.db.get_single_value("Accounts Settings", "add_taxes_from_item_tax_template")
if child_item.get("item_tax_rate") and add_taxes_from_item_tax_template:
@@ -1605,7 +1700,8 @@
"category" : "Total",
"add_deduct_tax" : "Add"
})
- tax_row.db_insert()
+ if db_insert:
+ tax_row.db_insert()
def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, trans_item):
"""
@@ -1882,4 +1978,4 @@
@erpnext.allow_regional
def validate_einvoice_fields(doc):
- pass
+ pass
\ No newline at end of file
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 6a550e0..974ade3 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -72,7 +72,8 @@
# set contact and address details for supplier, if they are not mentioned
if getattr(self, "supplier", None):
self.update_if_missing(get_party_details(self.supplier, party_type="Supplier", ignore_permissions=self.flags.ignore_permissions,
- doctype=self.doctype, company=self.company, party_address=self.supplier_address, shipping_address=self.get('shipping_address')))
+ doctype=self.doctype, company=self.company, party_address=self.supplier_address, shipping_address=self.get('shipping_address'),
+ fetch_payment_terms_template= not self.get('ignore_default_payment_terms_template')))
self.set_missing_item_details(for_validate)
diff --git a/erpnext/controllers/employee_boarding_controller.py b/erpnext/controllers/employee_boarding_controller.py
new file mode 100644
index 0000000..1898222
--- /dev/null
+++ b/erpnext/controllers/employee_boarding_controller.py
@@ -0,0 +1,127 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+from frappe import _
+from frappe.desk.form import assign_to
+from frappe.model.document import Document
+from frappe.utils import flt, unique
+
+class EmployeeBoardingController(Document):
+ '''
+ Create the project and the task for the boarding process
+ Assign to the concerned person and roles as per the onboarding/separation template
+ '''
+ def validate(self):
+ # remove the task if linked before submitting the form
+ if self.amended_from:
+ for activity in self.activities:
+ activity.task = ''
+
+ def on_submit(self):
+ # create the project for the given employee onboarding
+ project_name = _(self.doctype) + ' : '
+ if self.doctype == 'Employee Onboarding':
+ project_name += self.job_applicant
+ else:
+ project_name += self.employee
+
+ project = frappe.get_doc({
+ 'doctype': 'Project',
+ 'project_name': project_name,
+ 'expected_start_date': self.date_of_joining if self.doctype == 'Employee Onboarding' else self.resignation_letter_date,
+ 'department': self.department,
+ 'company': self.company
+ }).insert(ignore_permissions=True, ignore_mandatory=True)
+
+ self.db_set('project', project.name)
+ self.db_set('boarding_status', 'Pending')
+ self.reload()
+ self.create_task_and_notify_user()
+
+ def create_task_and_notify_user(self):
+ # create the task for the given project and assign to the concerned person
+ for activity in self.activities:
+ if activity.task:
+ continue
+
+ task = frappe.get_doc({
+ 'doctype': 'Task',
+ 'project': self.project,
+ 'subject': activity.activity_name + ' : ' + self.employee_name,
+ 'description': activity.description,
+ 'department': self.department,
+ 'company': self.company,
+ 'task_weight': activity.task_weight
+ }).insert(ignore_permissions=True)
+ activity.db_set('task', task.name)
+
+ users = [activity.user] if activity.user else []
+ if activity.role:
+ user_list = frappe.db.sql_list('''
+ SELECT
+ DISTINCT(has_role.parent)
+ FROM
+ `tabHas Role` has_role
+ LEFT JOIN `tabUser` user
+ ON has_role.parent = user.name
+ WHERE
+ has_role.parenttype = 'User'
+ AND user.enabled = 1
+ AND has_role.role = %s
+ ''', activity.role)
+ users = unique(users + user_list)
+
+ if 'Administrator' in users:
+ users.remove('Administrator')
+
+ # assign the task the users
+ if users:
+ self.assign_task_to_users(task, users)
+
+ def assign_task_to_users(self, task, users):
+ for user in users:
+ args = {
+ 'assign_to': [user],
+ 'doctype': task.doctype,
+ 'name': task.name,
+ 'description': task.description or task.subject,
+ 'notify': self.notify_users_by_email
+ }
+ assign_to.add(args)
+
+ def on_cancel(self):
+ # delete task project
+ for task in frappe.get_all('Task', filters={'project': self.project}):
+ frappe.delete_doc('Task', task.name, force=1)
+ frappe.delete_doc('Project', self.project, force=1)
+ self.db_set('project', '')
+ for activity in self.activities:
+ activity.db_set('task', '')
+
+
+@frappe.whitelist()
+def get_onboarding_details(parent, parenttype):
+ return frappe.get_all('Employee Boarding Activity',
+ fields=['activity_name', 'role', 'user', 'required_for_employee_creation', 'description', 'task_weight'],
+ filters={'parent': parent, 'parenttype': parenttype},
+ order_by= 'idx')
+
+
+def update_employee_boarding_status(project):
+ employee_onboarding = frappe.db.exists('Employee Onboarding', {'project': project.name})
+ employee_separation = frappe.db.exists('Employee Separation', {'project': project.name})
+
+ if not (employee_onboarding or employee_separation):
+ return
+
+ status = 'Pending'
+ if flt(project.percent_complete) > 0.0 and flt(project.percent_complete) < 100.0:
+ status = 'In Process'
+ elif flt(project.percent_complete) == 100.0:
+ status = 'Completed'
+
+ if employee_onboarding:
+ frappe.db.set_value('Employee Onboarding', employee_onboarding, 'boarding_status', status)
+ elif employee_separation:
+ frappe.db.set_value('Employee Separation', employee_separation, 'boarding_status', status)
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 2803193..21c052a 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -407,6 +407,7 @@
INNER JOIN `tabBatch` batch on sle.batch_no = batch.name
where
batch.disabled = 0
+ and sle.is_cancelled = 0
and sle.item_code = %(item_code)s
and sle.warehouse = %(warehouse)s
and (sle.batch_no like %(txt)s
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 2526e6d..17707ec 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -27,6 +27,7 @@
if not self.get('is_return'):
self.validate_inspection()
self.validate_serialized_batch()
+ self.clean_serial_nos()
self.validate_customer_provided_item()
self.set_rate_of_stock_uom()
self.validate_internal_transfer()
@@ -53,12 +54,17 @@
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
for d in self.get("items"):
if hasattr(d, 'serial_no') and hasattr(d, 'batch_no') and d.serial_no and d.batch_no:
- serial_nos = get_serial_nos(d.serial_no)
- for serial_no_data in frappe.get_all("Serial No",
- filters={"name": ("in", serial_nos)}, fields=["batch_no", "name"]):
- if serial_no_data.batch_no != d.batch_no:
+ serial_nos = frappe.get_all("Serial No",
+ fields=["batch_no", "name", "warehouse"],
+ filters={
+ "name": ("in", get_serial_nos(d.serial_no))
+ }
+ )
+
+ for row in serial_nos:
+ if row.warehouse and row.batch_no != d.batch_no:
frappe.throw(_("Row #{0}: Serial No {1} does not belong to Batch {2}")
- .format(d.idx, serial_no_data.name, d.batch_no))
+ .format(d.idx, row.name, d.batch_no))
if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2:
expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date")
@@ -67,6 +73,12 @@
frappe.throw(_("Row #{0}: The batch {1} has already expired.")
.format(d.idx, get_link_to_form("Batch", d.get("batch_no"))))
+ def clean_serial_nos(self):
+ for row in self.get("items"):
+ if hasattr(row, "serial_no") and row.serial_no:
+ # replace commas by linefeed and remove all spaces in string
+ row.serial_no = row.serial_no.replace(",", "\n").replace(" ", "")
+
def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
default_cost_center=None):
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 56da5b7..05edb25 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -152,7 +152,7 @@
validate_taxes_and_charges(tax)
validate_inclusive_tax(tax, self.doc)
- if not self.doc.get('is_consolidated'):
+ if not (self.doc.get('is_consolidated') or tax.get("dont_recompute_tax")):
tax.item_wise_tax_detail = {}
tax_fields = ["total", "tax_amount_after_discount_amount",
@@ -347,7 +347,7 @@
elif tax.charge_type == "On Item Quantity":
current_tax_amount = tax_rate * item.qty
- if not self.doc.get("is_consolidated"):
+ if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
return current_tax_amount
@@ -455,7 +455,8 @@
def _cleanup(self):
if not self.doc.get('is_consolidated'):
for tax in self.doc.get("taxes"):
- tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
+ if not tax.get("dont_recompute_tax"):
+ tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
def set_discount_amount(self):
if self.doc.additional_discount_percentage:
@@ -678,17 +679,13 @@
default_mode_of_payment = frappe.db.get_value('POS Payment Method',
{'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
- self.doc.payments = []
-
if default_mode_of_payment:
+ self.doc.payments = []
self.doc.append('payments', {
'mode_of_payment': default_mode_of_payment.mode_of_payment,
'amount': total_amount_to_pay,
'default': 1
})
- else:
- self.doc.is_pos = 0
- self.doc.pos_profile = ''
self.calculate_paid_amount()
diff --git a/erpnext/crm/doctype/appointment/test_appointment.py b/erpnext/crm/doctype/appointment/test_appointment.py
index 50c98c5..c7563e9 100644
--- a/erpnext/crm/doctype/appointment/test_appointment.py
+++ b/erpnext/crm/doctype/appointment/test_appointment.py
@@ -9,7 +9,7 @@
def create_test_lead():
- test_lead = frappe.db.exists({'doctype': 'Lead', 'lead_name': 'Test Lead'})
+ test_lead = frappe.db.exists({'doctype': 'Lead', 'email_id':'test@example.com'})
if test_lead:
return frappe.get_doc('Lead', test_lead[0][0])
test_lead = frappe.get_doc({
diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/__init__.py b/erpnext/crm/doctype/campaign/__init__.py
similarity index 100%
rename from erpnext/erpnext_integrations/doctype/shopify_log/__init__.py
rename to erpnext/crm/doctype/campaign/__init__.py
diff --git a/erpnext/crm/doctype/campaign/campaign.js b/erpnext/crm/doctype/campaign/campaign.js
new file mode 100644
index 0000000..11bfa74
--- /dev/null
+++ b/erpnext/crm/doctype/campaign/campaign.js
@@ -0,0 +1,17 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Campaign', {
+ refresh: function(frm) {
+ erpnext.toggle_naming_series();
+
+ if (frm.doc.__islocal) {
+ frm.toggle_display("naming_series", frappe.boot.sysdefaults.campaign_naming_by=="Naming Series");
+ } else {
+ cur_frm.add_custom_button(__("View Leads"), function() {
+ frappe.route_options = {"source": "Campaign", "campaign_name": frm.doc.name};
+ frappe.set_route("List", "Lead");
+ }, "fa fa-list", true);
+ }
+ }
+});
diff --git a/erpnext/selling/doctype/campaign/campaign.json b/erpnext/crm/doctype/campaign/campaign.json
similarity index 95%
rename from erpnext/selling/doctype/campaign/campaign.json
rename to erpnext/crm/doctype/campaign/campaign.json
index 986ac13..f833f4c 100644
--- a/erpnext/selling/doctype/campaign/campaign.json
+++ b/erpnext/crm/doctype/campaign/campaign.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "naming_series:",
@@ -39,17 +40,9 @@
"set_only_once": 1
},
{
- "fieldname": "description",
- "fieldtype": "Text",
- "in_list_view": 1,
- "label": "Description",
- "oldfieldname": "description",
- "oldfieldtype": "Text",
- "width": "300px"
- },
- {
- "fieldname": "description_section",
- "fieldtype": "Section Break"
+ "fieldname": "campaign_schedules_section",
+ "fieldtype": "Section Break",
+ "label": "Campaign Schedules"
},
{
"fieldname": "campaign_schedules",
@@ -58,16 +51,25 @@
"options": "Campaign Email Schedule"
},
{
- "fieldname": "campaign_schedules_section",
- "fieldtype": "Section Break",
- "label": "Campaign Schedules"
+ "fieldname": "description_section",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "description",
+ "fieldtype": "Text",
+ "in_list_view": 1,
+ "label": "Description",
+ "oldfieldname": "description",
+ "oldfieldtype": "Text",
+ "width": "300px"
}
],
"icon": "fa fa-bullhorn",
"idx": 1,
- "modified": "2019-07-22 12:03:39.832342",
+ "links": [],
+ "modified": "2021-06-30 18:05:06.412712",
"modified_by": "Administrator",
- "module": "Selling",
+ "module": "CRM",
"name": "Campaign",
"owner": "Administrator",
"permissions": [
diff --git a/erpnext/selling/doctype/campaign/campaign.py b/erpnext/crm/doctype/campaign/campaign.py
similarity index 66%
rename from erpnext/selling/doctype/campaign/campaign.py
rename to erpnext/crm/doctype/campaign/campaign.py
index 1094542..e32799f 100644
--- a/erpnext/selling/doctype/campaign/campaign.py
+++ b/erpnext/crm/doctype/campaign/campaign.py
@@ -1,9 +1,7 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
-from __future__ import unicode_literals
import frappe
-
from frappe.model.document import Document
from frappe.model.naming import set_name_by_naming_series
diff --git a/erpnext/crm/doctype/campaign/test_campaign.py b/erpnext/crm/doctype/campaign/test_campaign.py
new file mode 100644
index 0000000..7124b8c
--- /dev/null
+++ b/erpnext/crm/doctype/campaign/test_campaign.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+class TestCampaign(unittest.TestCase):
+ pass
diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js
index ebe8524..75af937 100644
--- a/erpnext/crm/doctype/lead/lead.js
+++ b/erpnext/crm/doctype/lead/lead.js
@@ -12,7 +12,8 @@
'Opportunity': this.make_opportunity
};
- this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
+ // For avoiding integration issues.
+ this.frm.set_df_property('first_name', 'reqd', true);
}
onload () {
@@ -42,6 +43,7 @@
if (!this.frm.is_new()) {
frappe.contacts.render_address_and_contact(this.frm);
+ cur_frm.trigger('render_contact_day_html');
} else {
frappe.contacts.clear_address_and_contact(this.frm);
}
@@ -68,13 +70,8 @@
})
}
- organization_lead () {
- this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
- this.frm.toggle_reqd("company_name", this.frm.doc.organization_lead);
- }
-
company_name () {
- if (this.frm.doc.organization_lead && !this.frm.doc.lead_name) {
+ if (!this.frm.doc.lead_name) {
this.frm.set_value("lead_name", this.frm.doc.company_name);
}
}
@@ -86,6 +83,19 @@
this.frm.set_value("ends_on", d.format(frappe.defaultDatetimeFormat));
}
}
+
+ render_contact_day_html() {
+ if (cur_frm.doc.contact_date) {
+ let contact_date = frappe.datetime.obj_to_str(cur_frm.doc.contact_date);
+ let diff_days = frappe.datetime.get_day_diff(contact_date, frappe.datetime.get_today());
+ let color = diff_days > 0 ? "orange" : "green";
+ let message = diff_days > 0 ? __("Next Contact Date") : __("Last Contact Date");
+ let html = `<div class="col-xs-12">
+ <span class="indicator whitespace-nowrap ${color}"><span> ${message} : ${frappe.datetime.global_date_format(contact_date)}</span></span>
+ </div>` ;
+ cur_frm.dashboard.set_headline_alert(html);
+ }
+ }
};
extend_cscript(cur_frm.cscript, new erpnext.LeadController({ frm: cur_frm }));
diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json
index 1b33fd7..542977e 100644
--- a/erpnext/crm/doctype/lead/lead.json
+++ b/erpnext/crm/doctype/lead/lead.json
@@ -9,71 +9,70 @@
"email_append_to": 1,
"engine": "InnoDB",
"field_order": [
- "organization_lead",
"lead_details",
"naming_series",
- "lead_name",
- "company_name",
- "email_id",
- "col_break123",
- "lead_owner",
- "status",
"salutation",
+ "first_name",
+ "middle_name",
+ "last_name",
+ "lead_name",
+ "col_break123",
+ "status",
+ "company_name",
"designation",
"gender",
- "source",
- "customer",
- "campaign_name",
- "image",
- "section_break_12",
- "contact_by",
- "column_break_14",
- "contact_date",
- "ends_on",
- "notes_section",
- "notes",
- "address_info",
+ "contact_details_section",
+ "email_id",
+ "mobile_no",
+ "whatsapp_no",
+ "column_break_16",
+ "phone",
+ "phone_ext",
+ "additional_information_section",
+ "no_of_employees",
+ "industry",
+ "market_segment",
+ "column_break_22",
+ "fax",
+ "website",
+ "type",
+ "request_type",
+ "address_section",
"address_html",
- "address_type",
- "address_title",
- "address_line1",
- "address_line2",
"city",
+ "pincode",
"county",
"column_break2",
"contact_html",
"state",
"country",
- "pincode",
- "contact_section",
- "phone",
- "mobile_no",
- "fax",
- "website",
- "more_info",
- "type",
- "market_segment",
- "industry",
- "request_type",
- "column_break3",
+ "section_break_12",
+ "lead_owner",
+ "ends_on",
+ "column_break_14",
+ "contact_by",
+ "contact_date",
+ "lead_source_details_section",
"company",
"territory",
"language",
+ "column_break_50",
+ "source",
+ "campaign_name",
"unsubscribed",
"blog_subscriber",
+ "notes_section",
+ "notes",
+ "other_information_section",
+ "customer",
+ "image",
"title"
],
"fields": [
{
- "default": "0",
- "fieldname": "organization_lead",
- "fieldtype": "Check",
- "label": "Lead is an Organization",
- "set_only_once": 1
- },
- {
"fieldname": "lead_details",
"fieldtype": "Section Break",
+ "label": "Lead Details",
"options": "fa fa-user"
},
{
@@ -90,16 +89,19 @@
"fieldname": "lead_name",
"fieldtype": "Data",
"in_global_search": 1,
- "label": "Person Name",
+ "label": "Full Name",
"oldfieldname": "lead_name",
"oldfieldtype": "Data",
+ "read_only": 1,
"search_index": 1
},
{
"fieldname": "company_name",
"fieldtype": "Data",
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Organization Name",
+ "mandatory_depends_on": "eval: !(doc.first_name)",
"oldfieldname": "company_name",
"oldfieldtype": "Data"
},
@@ -121,7 +123,6 @@
"default": "__user",
"fieldname": "lead_owner",
"fieldtype": "Link",
- "in_list_view": 1,
"label": "Lead Owner",
"oldfieldname": "lead_owner",
"oldfieldtype": "Link",
@@ -143,7 +144,6 @@
"search_index": 1
},
{
- "depends_on": "eval: doc.__islocal",
"fieldname": "salutation",
"fieldtype": "Link",
"label": "Salutation",
@@ -241,46 +241,22 @@
"read_only": 1
},
{
- "depends_on": "eval: doc.__islocal",
- "description": "Home, Work, etc.",
- "fieldname": "address_title",
- "fieldtype": "Data",
- "label": "Address Title"
- },
- {
- "depends_on": "eval: doc.__islocal",
- "fieldname": "address_line1",
- "fieldtype": "Data",
- "label": "Address Line 1",
- "mandatory_depends_on": "eval: doc.address_title && doc.address_type"
- },
- {
- "depends_on": "eval: doc.__islocal",
- "fieldname": "address_line2",
- "fieldtype": "Data",
- "label": "Address Line 2"
- },
- {
- "depends_on": "eval: doc.__islocal",
"fieldname": "city",
"fieldtype": "Data",
"label": "City/Town",
"mandatory_depends_on": "eval: doc.address_title && doc.address_type"
},
{
- "depends_on": "eval: doc.__islocal",
"fieldname": "county",
"fieldtype": "Data",
"label": "County"
},
{
- "depends_on": "eval: doc.__islocal",
"fieldname": "state",
"fieldtype": "Data",
"label": "State"
},
{
- "depends_on": "eval: doc.__islocal",
"fieldname": "country",
"fieldtype": "Link",
"label": "Country",
@@ -288,7 +264,6 @@
"options": "Country"
},
{
- "depends_on": "eval: doc.__islocal",
"fieldname": "pincode",
"fieldtype": "Data",
"label": "Postal Code"
@@ -304,7 +279,6 @@
"read_only": 1
},
{
- "depends_on": "eval: doc.__islocal",
"fieldname": "phone",
"fieldtype": "Data",
"label": "Phone",
@@ -313,7 +287,6 @@
"options": "Phone"
},
{
- "depends_on": "eval: doc.__islocal",
"fieldname": "mobile_no",
"fieldtype": "Data",
"label": "Mobile No.",
@@ -322,7 +295,6 @@
"options": "Phone"
},
{
- "depends_on": "eval: doc.__islocal",
"fieldname": "fax",
"fieldtype": "Data",
"label": "Fax",
@@ -330,14 +302,6 @@
"oldfieldtype": "Data"
},
{
- "collapsible": 1,
- "fieldname": "more_info",
- "fieldtype": "Section Break",
- "label": "More Information",
- "oldfieldtype": "Section Break",
- "options": "fa fa-file-text"
- },
- {
"fieldname": "type",
"fieldtype": "Select",
"label": "Lead Type",
@@ -370,12 +334,6 @@
"options": "\nProduct Enquiry\nRequest for Information\nSuggestions\nOther"
},
{
- "fieldname": "column_break3",
- "fieldtype": "Column Break",
- "oldfieldtype": "Column Break",
- "width": "50%"
- },
- {
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
@@ -389,11 +347,14 @@
"fieldtype": "Data",
"label": "Website",
"oldfieldname": "website",
- "oldfieldtype": "Data"
+ "oldfieldtype": "Data",
+ "options": "URL"
},
{
"fieldname": "territory",
"fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Territory",
"oldfieldname": "territory",
"oldfieldtype": "Link",
@@ -422,45 +383,95 @@
{
"fieldname": "designation",
"fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Designation",
"options": "Designation"
},
{
- "collapsible": 1,
- "collapsible_depends_on": "eval: doc.__islocal",
- "fieldname": "address_info",
- "fieldtype": "Section Break",
- "label": "Address & Contact",
- "oldfieldtype": "Column Break",
- "options": "fa fa-map-marker"
- },
- {
- "collapsible": 1,
- "collapsible_depends_on": "eval: doc.__islocal",
- "fieldname": "contact_section",
- "fieldtype": "Section Break",
- "label": "Contact"
- },
- {
- "default": "Billing",
- "depends_on": "eval: doc.__islocal",
- "fieldname": "address_type",
- "fieldtype": "Select",
- "label": "Address Type",
- "options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nCurrent\nPermanent\nOther"
- },
- {
"fieldname": "language",
"fieldtype": "Link",
"label": "Print Language",
"options": "Language"
+ },
+ {
+ "fieldname": "first_name",
+ "fieldtype": "Data",
+ "label": "First Name",
+ "mandatory_depends_on": "eval: !(doc.company_name)"
+ },
+ {
+ "fieldname": "middle_name",
+ "fieldtype": "Data",
+ "label": "Middle Name"
+ },
+ {
+ "fieldname": "last_name",
+ "fieldtype": "Data",
+ "label": "Last Name"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "additional_information_section",
+ "fieldtype": "Section Break",
+ "label": "Additional Information"
+ },
+ {
+ "fieldname": "no_of_employees",
+ "fieldtype": "Int",
+ "label": "No. of Employees"
+ },
+ {
+ "fieldname": "column_break_22",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "whatsapp_no",
+ "fieldtype": "Data",
+ "label": "WhatsApp No.",
+ "options": "Phone"
+ },
+ {
+ "collapsible": 1,
+ "depends_on": "eval: !doc.__islocal",
+ "fieldname": "address_section",
+ "fieldtype": "Section Break",
+ "label": "Address"
+ },
+ {
+ "fieldname": "lead_source_details_section",
+ "fieldtype": "Section Break",
+ "label": "Lead Source Details"
+ },
+ {
+ "fieldname": "column_break_50",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "other_information_section",
+ "fieldtype": "Section Break",
+ "label": "Other Information"
+ },
+ {
+ "fieldname": "contact_details_section",
+ "fieldtype": "Section Break",
+ "label": "Contact Details"
+ },
+ {
+ "fieldname": "column_break_16",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "phone_ext",
+ "fieldtype": "Data",
+ "label": "Phone Ext."
}
],
"icon": "fa fa-user",
"idx": 5,
"image_field": "image",
"links": [],
- "modified": "2021-01-06 19:39:58.748978",
+ "modified": "2021-08-04 00:24:57.208590",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",
diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py
index ce3de40..7f028cb 100644
--- a/erpnext/crm/doctype/lead/lead.py
+++ b/erpnext/crm/doctype/lead/lead.py
@@ -21,26 +21,24 @@
self.get("__onload").is_customer = customer
load_address_and_contact(self)
- def before_insert(self):
- if self.address_title and self.address_type:
- self.address_doc = self.create_address()
- self.contact_doc = self.create_contact()
-
- def after_insert(self):
- self.update_links()
-
def validate(self):
+ self.set_full_name()
self.set_lead_name()
self.set_title()
+ self.set_status()
+ self.check_email_id_is_unique()
+ self.validate_email_id()
+ self.validate_contact_date()
self._prev = frappe._dict({
"contact_date": frappe.db.get_value("Lead", self.name, "contact_date") if (not cint(self.is_new())) else None,
"ends_on": frappe.db.get_value("Lead", self.name, "ends_on") if (not cint(self.is_new())) else None,
"contact_by": frappe.db.get_value("Lead", self.name, "contact_by") if (not cint(self.is_new())) else None,
})
+
+ def set_full_name(self):
+ self.lead_name = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name]))
- self.set_status()
- self.check_email_id_is_unique()
-
+ def validate_email_id(self):
if self.email_id:
if not self.flags.ignore_email_validation:
validate_email_address(self.email_id, throw=True)
@@ -54,6 +52,7 @@
if self.is_new() or not self.image:
self.image = has_gravatar(self.email_id)
+ def validate_contact_date(self):
if self.contact_date and getdate(self.contact_date) < getdate(nowdate()):
frappe.throw(_("Next Contact Date cannot be in the past"))
@@ -64,6 +63,22 @@
def on_update(self):
self.add_calendar_event()
+ def before_insert(self):
+ self.contact_doc = self.create_contact()
+
+ def after_insert(self):
+ self.update_links()
+
+ def update_links(self):
+ # update contact links
+ if self.contact_doc:
+ self.contact_doc.append("links", {
+ "link_doctype": "Lead",
+ "link_name": self.name,
+ "link_title": self.lead_name
+ })
+ self.contact_doc.save()
+
def add_calendar_event(self, opts=None, force=False):
super(Lead, self).add_calendar_event({
"owner": self.lead_owner,
@@ -86,8 +101,26 @@
def on_trash(self):
frappe.db.sql("""update `tabIssue` set lead='' where lead=%s""", self.name)
+ self.unlink_dynamic_links()
self.delete_events()
+ def unlink_dynamic_links(self):
+ links = frappe.get_all('Dynamic Link', filters={'link_doctype': self.doctype, 'link_name': self.name}, fields=['parent', 'parenttype'])
+
+ for link in links:
+ linked_doc = frappe.get_doc(link['parenttype'], link['parent'])
+
+ if len(linked_doc.get('links')) == 1:
+ linked_doc.delete(ignore_permissions=True)
+ else:
+ to_remove = None
+ for d in linked_doc.get('links'):
+ if d.link_doctype == self.doctype and d.link_name == self.name:
+ to_remove = d
+ if to_remove:
+ linked_doc.remove(to_remove)
+ linked_doc.save(ignore_permissions=True)
+
def has_customer(self):
return frappe.db.get_value("Customer", {"lead_name": self.name})
@@ -99,7 +132,6 @@
"party_name": self.name,
"docstatus": 1,
"status": ["!=", "Lost"]
-
})
def has_lost_quotation(self):
@@ -120,40 +152,17 @@
self.lead_name = self.email_id.split("@")[0]
def set_title(self):
- if self.organization_lead:
- self.title = self.company_name
- else:
- self.title = self.lead_name
-
- def create_address(self):
- address_fields = ["address_type", "address_title", "address_line1", "address_line2",
- "city", "county", "state", "country", "pincode"]
- info_fields = ["email_id", "phone", "fax"]
-
- # do not create an address if no fields are available,
- # skipping country since the system auto-sets it from system defaults
- address = frappe.new_doc("Address")
-
- address.update({addr_field: self.get(addr_field) for addr_field in address_fields})
- address.update({info_field: self.get(info_field) for info_field in info_fields})
- address.insert()
-
- return address
+ self.title = self.company_name or self.lead_name
def create_contact(self):
if not self.lead_name:
+ self.set_full_name()
self.set_lead_name()
- names = self.lead_name.strip().split(" ")
- if len(names) > 1:
- first_name, last_name = names[0], " ".join(names[1:])
- else:
- first_name, last_name = self.lead_name, None
-
contact = frappe.new_doc("Contact")
contact.update({
- "first_name": first_name,
- "last_name": last_name,
+ "first_name": self.first_name or self.lead_name,
+ "last_name": self.last_name,
"salutation": self.salutation,
"gender": self.gender,
"designation": self.designation,
@@ -181,25 +190,6 @@
return contact
- def update_links(self):
- # update address links
- if hasattr(self, 'address_doc'):
- self.address_doc.append("links", {
- "link_doctype": "Lead",
- "link_name": self.name,
- "link_title": self.lead_name
- })
- self.address_doc.save()
-
- # update contact links
- if self.contact_doc:
- self.contact_doc.append("links", {
- "link_doctype": "Lead",
- "link_name": self.name,
- "link_title": self.lead_name
- })
- self.contact_doc.save()
-
@frappe.whitelist()
def make_customer(source_name, target_doc=None):
return _make_customer(source_name, target_doc)
diff --git a/erpnext/crm/doctype/lead/test_lead.py b/erpnext/crm/doctype/lead/test_lead.py
index d428a45..d4886d3 100644
--- a/erpnext/crm/doctype/lead/test_lead.py
+++ b/erpnext/crm/doctype/lead/test_lead.py
@@ -4,6 +4,7 @@
from __future__ import unicode_literals
import frappe
+from frappe.utils import random_string
import unittest
test_records = frappe.get_test_records('Lead')
@@ -32,3 +33,53 @@
customer.company = "_Test Company"
customer.customer_group = "_Test Customer Group"
customer.insert()
+
+ def test_create_lead_and_unlinking_dynamic_links(self):
+ lead_doc = make_lead(first_name = "Lorem", last_name="Ipsum", email_id="lorem_ipsum@example.com")
+ lead_doc_1 = make_lead()
+ frappe.get_doc({
+ "doctype": "Address",
+ "address_type": "Billing",
+ "city": "Mumbai",
+ "address_line1": "Vidya Vihar West",
+ "country": "India",
+ "links": [{
+ "link_doctype": "Lead",
+ "link_name": lead_doc.name
+ }]
+ }).insert()
+
+ address_1 = frappe.get_doc({
+ "doctype": "Address",
+ "address_type": "Billing",
+ "address_line1": "Baner",
+ "city": "Pune",
+ "country": "India",
+ "links": [
+ {
+ "link_doctype": "Lead",
+ "link_name": lead_doc.name
+ },
+ {
+ "link_doctype": "Lead",
+ "link_name": lead_doc_1.name
+ }
+ ]
+ }).insert()
+
+ lead_doc.delete()
+ address_1.reload()
+ self.assertEqual(frappe.db.exists("Lead",lead_doc.name), None)
+ self.assertEqual(len(address_1.get('links')), 1)
+
+def make_lead(**args):
+ args = frappe._dict(args)
+
+ lead_doc = frappe.get_doc({
+ "doctype": "Lead",
+ "first_name": args.first_name or "_Test",
+ "last_name": args.last_name or "Lead",
+ "email_id": args.email_id or "new_lead_{}@example.com".format(random_string(5)),
+ }).insert()
+
+ return lead_doc
\ No newline at end of file
diff --git a/erpnext/crm/doctype/lead/test_records.json b/erpnext/crm/doctype/lead/test_records.json
index 39864e2..3158add 100644
--- a/erpnext/crm/doctype/lead/test_records.json
+++ b/erpnext/crm/doctype/lead/test_records.json
@@ -27,7 +27,6 @@
{
"doctype": "Lead",
"email_id": "test_lead4@example.com",
- "organization_lead": 1,
"lead_name": "_Test Lead 4",
"company_name": "_Test Lead 4",
"status": "Open"
diff --git a/erpnext/crm/doctype/lead/tests/test_lead_organization.js b/erpnext/crm/doctype/lead/tests/test_lead_organization.js
index 4395935..7fb9573 100644
--- a/erpnext/crm/doctype/lead/tests/test_lead_organization.js
+++ b/erpnext/crm/doctype/lead/tests/test_lead_organization.js
@@ -9,7 +9,6 @@
() => frappe.set_route("List", "Lead"),
() => frappe.new_doc("Lead"),
() => frappe.timeout(1),
- () => cur_frm.set_value("organization_lead", "1"),
() => cur_frm.set_value("company_name", lead_name),
() => cur_frm.save(),
() => frappe.timeout(1),
diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js
index 43e1b99..e9a7a95 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.js
+++ b/erpnext/crm/doctype/opportunity/opportunity.js
@@ -53,6 +53,13 @@
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
},
+ status:function(frm){
+ if (frm.doc.status == "Lost"){
+ frm.trigger('set_as_lost_dialog');
+ }
+
+ },
+
customer_address: function(frm, cdt, cdn) {
erpnext.utils.get_address_display(frm, 'customer_address', 'address_display', false);
},
@@ -91,11 +98,6 @@
frm.add_custom_button(__('Quotation'),
cur_frm.cscript.create_quotation, __('Create'));
- if(doc.status!=="Quotation") {
- frm.add_custom_button(__('Lost'), () => {
- frm.trigger('set_as_lost_dialog');
- });
- }
}
if(!frm.doc.__islocal && frm.perm[0].write && frm.doc.docstatus==0) {
diff --git a/erpnext/crm/workspace/crm/crm.json b/erpnext/crm/workspace/crm/crm.json
index b4fb7d8..c363395 100644
--- a/erpnext/crm/workspace/crm/crm.json
+++ b/erpnext/crm/workspace/crm/crm.json
@@ -1,26 +1,31 @@
{
- "category": "Modules",
+ "category": "",
"charts": [
{
"chart_name": "Territory Wise Sales"
}
],
+ "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"CRM\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": null, \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Lead\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Opportunity\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Customer\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Analytics\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Sales Pipeline\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Maintenance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Campaign\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}]",
"creation": "2020-01-23 14:48:30.183272",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "crm",
"idx": 0,
- "is_standard": 1,
+ "is_default": 0,
+ "is_standard": 0,
"label": "CRM",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Sales Pipeline",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -29,6 +34,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Lead",
+ "link_count": 0,
"link_to": "Lead",
"link_type": "DocType",
"onboard": 1,
@@ -39,6 +45,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Opportunity",
+ "link_count": 0,
"link_to": "Opportunity",
"link_type": "DocType",
"onboard": 1,
@@ -49,6 +56,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Customer",
+ "link_count": 0,
"link_to": "Customer",
"link_type": "DocType",
"onboard": 1,
@@ -59,6 +67,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Contact",
+ "link_count": 0,
"link_to": "Contact",
"link_type": "DocType",
"onboard": 1,
@@ -69,6 +78,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Communication",
+ "link_count": 0,
"link_to": "Communication",
"link_type": "DocType",
"onboard": 0,
@@ -79,6 +89,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Lead Source",
+ "link_count": 0,
"link_to": "Lead Source",
"link_type": "DocType",
"onboard": 0,
@@ -89,6 +100,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Contract",
+ "link_count": 0,
"link_to": "Contract",
"link_type": "DocType",
"onboard": 0,
@@ -99,6 +111,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Appointment",
+ "link_count": 0,
"link_to": "Appointment",
"link_type": "DocType",
"onboard": 0,
@@ -109,6 +122,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Newsletter",
+ "link_count": 0,
"link_to": "Newsletter",
"link_type": "DocType",
"onboard": 0,
@@ -118,6 +132,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -126,6 +141,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Lead Details",
+ "link_count": 0,
"link_to": "Lead Details",
"link_type": "Report",
"onboard": 1,
@@ -136,6 +152,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Sales Funnel",
+ "link_count": 0,
"link_to": "sales-funnel",
"link_type": "Page",
"onboard": 1,
@@ -146,6 +163,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Prospects Engaged But Not Converted",
+ "link_count": 0,
"link_to": "Prospects Engaged But Not Converted",
"link_type": "Report",
"onboard": 1,
@@ -156,6 +174,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "First Response Time for Opportunity",
+ "link_count": 0,
"link_to": "First Response Time for Opportunity",
"link_type": "Report",
"onboard": 0,
@@ -166,6 +185,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Inactive Customers",
+ "link_count": 0,
"link_to": "Inactive Customers",
"link_type": "Report",
"onboard": 0,
@@ -176,6 +196,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Campaign Efficiency",
+ "link_count": 0,
"link_to": "Campaign Efficiency",
"link_type": "Report",
"onboard": 0,
@@ -186,6 +207,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Lead Owner Efficiency",
+ "link_count": 0,
"link_to": "Lead Owner Efficiency",
"link_type": "Report",
"onboard": 0,
@@ -195,6 +217,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Maintenance",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -203,6 +226,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Maintenance Schedule",
+ "link_count": 0,
"link_to": "Maintenance Schedule",
"link_type": "DocType",
"onboard": 1,
@@ -213,6 +237,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Maintenance Visit",
+ "link_count": 0,
"link_to": "Maintenance Visit",
"link_type": "DocType",
"onboard": 0,
@@ -223,6 +248,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Warranty Claim",
+ "link_count": 0,
"link_to": "Warranty Claim",
"link_type": "DocType",
"onboard": 0,
@@ -232,6 +258,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Campaign",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -240,6 +267,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Campaign",
+ "link_count": 0,
"link_to": "Campaign",
"link_type": "DocType",
"onboard": 0,
@@ -250,6 +278,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Email Campaign",
+ "link_count": 0,
"link_to": "Email Campaign",
"link_type": "DocType",
"onboard": 0,
@@ -260,6 +289,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Social Media Post",
+ "link_count": 0,
"link_to": "Social Media Post",
"link_type": "DocType",
"onboard": 0,
@@ -269,6 +299,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Settings",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -277,6 +308,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Customer Group",
+ "link_count": 0,
"link_to": "Customer Group",
"link_type": "DocType",
"onboard": 1,
@@ -287,6 +319,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Territory",
+ "link_count": 0,
"link_to": "Territory",
"link_type": "DocType",
"onboard": 1,
@@ -297,6 +330,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Sales Person",
+ "link_count": 0,
"link_to": "Sales Person",
"link_type": "DocType",
"onboard": 1,
@@ -307,6 +341,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "SMS Center",
+ "link_count": 0,
"link_to": "SMS Center",
"link_type": "DocType",
"onboard": 0,
@@ -317,6 +352,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "SMS Log",
+ "link_count": 0,
"link_to": "SMS Log",
"link_type": "DocType",
"onboard": 0,
@@ -327,6 +363,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "SMS Settings",
+ "link_count": 0,
"link_to": "SMS Settings",
"link_type": "DocType",
"onboard": 0,
@@ -337,6 +374,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Email Group",
+ "link_count": 0,
"link_to": "Email Group",
"link_type": "DocType",
"onboard": 0,
@@ -347,6 +385,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Twitter Settings",
+ "link_count": 0,
"link_to": "Twitter Settings",
"link_type": "DocType",
"onboard": 0,
@@ -357,20 +396,26 @@
"hidden": 0,
"is_query_report": 0,
"label": "LinkedIn Settings",
+ "link_count": 0,
"link_to": "LinkedIn Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2020-12-01 13:38:36.871352",
+ "modified": "2021-08-05 12:15:56.913091",
"modified_by": "Administrator",
"module": "CRM",
"name": "CRM",
"onboarding": "CRM",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 7,
"shortcuts": [
{
"color": "Blue",
@@ -403,5 +448,6 @@
"link_to": "CRM",
"type": "Dashboard"
}
- ]
+ ],
+ "title": "CRM"
}
\ No newline at end of file
diff --git a/erpnext/education/api.py b/erpnext/education/api.py
index afa0be9..4493a3f 100644
--- a/erpnext/education/api.py
+++ b/erpnext/education/api.py
@@ -34,11 +34,14 @@
}
}}, ignore_permissions=True)
student.save()
+
+ student_applicant = frappe.db.get_value("Student Applicant", source_name,
+ ["student_category", "program"], as_dict=True)
program_enrollment = frappe.new_doc("Program Enrollment")
program_enrollment.student = student.name
- program_enrollment.student_category = student.student_category
+ program_enrollment.student_category = student_applicant.student_category
program_enrollment.student_name = student.title
- program_enrollment.program = frappe.db.get_value("Student Applicant", source_name, "program")
+ program_enrollment.program = student_applicant.program
frappe.publish_realtime('enroll_student_progress', {"progress": [2, 4]}, user=frappe.session.user)
return program_enrollment
diff --git a/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.json b/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.json
index 9be292b..1d74973 100644
--- a/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.json
+++ b/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.json
@@ -1,195 +1,68 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2016-06-10 03:29:02.539914",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
+ "actions": [],
+ "creation": "2016-06-10 03:29:02.539914",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "student_applicant",
+ "student",
+ "student_name",
+ "column_break_3",
+ "student_batch_name",
+ "student_category"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fieldname": "student_applicant",
- "fieldtype": "Link",
- "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": "Student Applicant",
- "length": 0,
- "no_copy": 0,
- "options": "Student Applicant",
- "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
- },
+ "fieldname": "student_applicant",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Student Applicant",
+ "options": "Student Applicant"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fieldname": "student",
- "fieldtype": "Link",
- "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": "Student",
- "length": 0,
- "no_copy": 0,
- "options": "Student",
- "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
- },
+ "fieldname": "student",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Student",
+ "options": "Student"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_3",
- "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
- },
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "student_name",
- "fieldtype": "Data",
- "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": "Student Name",
- "length": 0,
- "no_copy": 0,
- "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,
- "unique": 0
- },
+ "fieldname": "student_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Student Name",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "student_batch_name",
- "fieldtype": "Link",
- "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": "Student Batch Name",
- "length": 0,
- "no_copy": 0,
- "options": "Student Batch Name",
- "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
+ "fieldname": "student_batch_name",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Student Batch Name",
+ "options": "Student Batch Name"
+ },
+ {
+ "fieldname": "student_category",
+ "fieldtype": "Link",
+ "label": "Student Category",
+ "options": "Student Category",
+ "read_only": 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,
- "max_attachments": 0,
- "modified": "2018-01-02 12:03:53.890741",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Program Enrollment Tool Student",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Education",
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 0,
- "track_seen": 0
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2021-07-29 18:19:54.471594",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Program Enrollment Tool Student",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "restrict_to_domain": "Education",
+ "sort_field": "modified",
+ "sort_order": "DESC"
}
\ No newline at end of file
diff --git a/erpnext/education/workspace/education/education.json b/erpnext/education/workspace/education/education.json
index bf74961..c58ddd6 100644
--- a/erpnext/education/workspace/education/education.json
+++ b/erpnext/education/workspace/education/education.json
@@ -1,27 +1,32 @@
{
- "category": "Domains",
+ "category": "",
"charts": [
{
"chart_name": "Program Enrollments",
"label": "Program Enrollments"
}
],
+ "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Education\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Program Enrollments\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Student\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Instructor\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Program\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Course\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Fees\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Student Monthly Attendance Sheet\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Course Scheduling Tool\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Student Attendance Tool\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Student and Instructor\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Content Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Admission\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Fees\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Schedule\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Attendance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"LMS Activity\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Assessment\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Assessment Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tools\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}]",
"creation": "2020-03-02 17:22:57.066401",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "education",
"idx": 0,
- "is_standard": 1,
+ "is_default": 0,
+ "is_standard": 0,
"label": "Education",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Student and Instructor",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -30,6 +35,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Student",
+ "link_count": 0,
"link_to": "Student",
"link_type": "DocType",
"onboard": 1,
@@ -40,6 +46,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Instructor",
+ "link_count": 0,
"link_to": "Instructor",
"link_type": "DocType",
"onboard": 1,
@@ -50,6 +57,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Guardian",
+ "link_count": 0,
"link_to": "Guardian",
"link_type": "DocType",
"onboard": 0,
@@ -60,6 +68,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Student Group",
+ "link_count": 0,
"link_to": "Student Group",
"link_type": "DocType",
"onboard": 0,
@@ -70,6 +79,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Student Log",
+ "link_count": 0,
"link_to": "Student Log",
"link_type": "DocType",
"onboard": 0,
@@ -79,6 +89,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Masters",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -87,6 +98,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Program",
+ "link_count": 0,
"link_to": "Program",
"link_type": "DocType",
"onboard": 0,
@@ -97,6 +109,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Course",
+ "link_count": 0,
"link_to": "Course",
"link_type": "DocType",
"onboard": 1,
@@ -107,6 +120,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Topic",
+ "link_count": 0,
"link_to": "Topic",
"link_type": "DocType",
"onboard": 0,
@@ -117,6 +131,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Room",
+ "link_count": 0,
"link_to": "Room",
"link_type": "DocType",
"onboard": 1,
@@ -126,6 +141,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Content Masters",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -134,6 +150,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Article",
+ "link_count": 0,
"link_to": "Article",
"link_type": "DocType",
"onboard": 0,
@@ -144,6 +161,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Video",
+ "link_count": 0,
"link_to": "Video",
"link_type": "DocType",
"onboard": 0,
@@ -154,6 +172,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Quiz",
+ "link_count": 0,
"link_to": "Quiz",
"link_type": "DocType",
"onboard": 0,
@@ -163,6 +182,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Settings",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -171,6 +191,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Education Settings",
+ "link_count": 0,
"link_to": "Education Settings",
"link_type": "DocType",
"onboard": 0,
@@ -181,6 +202,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Student Category",
+ "link_count": 0,
"link_to": "Student Category",
"link_type": "DocType",
"onboard": 0,
@@ -191,6 +213,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Student Batch Name",
+ "link_count": 0,
"link_to": "Student Batch Name",
"link_type": "DocType",
"onboard": 0,
@@ -201,6 +224,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Grading Scale",
+ "link_count": 0,
"link_to": "Grading Scale",
"link_type": "DocType",
"onboard": 1,
@@ -211,6 +235,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Academic Term",
+ "link_count": 0,
"link_to": "Academic Term",
"link_type": "DocType",
"onboard": 0,
@@ -221,6 +246,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Academic Year",
+ "link_count": 0,
"link_to": "Academic Year",
"link_type": "DocType",
"onboard": 0,
@@ -230,6 +256,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Admission",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -238,6 +265,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Student Applicant",
+ "link_count": 0,
"link_to": "Student Applicant",
"link_type": "DocType",
"onboard": 0,
@@ -248,6 +276,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Student Admission",
+ "link_count": 0,
"link_to": "Student Admission",
"link_type": "DocType",
"onboard": 0,
@@ -258,6 +287,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Program Enrollment",
+ "link_count": 0,
"link_to": "Program Enrollment",
"link_type": "DocType",
"onboard": 0,
@@ -268,6 +298,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Course Enrollment",
+ "link_count": 0,
"link_to": "Course Enrollment",
"link_type": "DocType",
"onboard": 0,
@@ -277,6 +308,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Fees",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -285,6 +317,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Fee Structure",
+ "link_count": 0,
"link_to": "Fee Structure",
"link_type": "DocType",
"onboard": 0,
@@ -295,6 +328,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Fee Category",
+ "link_count": 0,
"link_to": "Fee Category",
"link_type": "DocType",
"onboard": 0,
@@ -305,6 +339,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Fee Schedule",
+ "link_count": 0,
"link_to": "Fee Schedule",
"link_type": "DocType",
"onboard": 0,
@@ -315,6 +350,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Fees",
+ "link_count": 0,
"link_to": "Fees",
"link_type": "DocType",
"onboard": 0,
@@ -325,6 +361,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Student Fee Collection Report",
+ "link_count": 0,
"link_to": "Student Fee Collection",
"link_type": "Report",
"onboard": 0,
@@ -335,6 +372,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Program wise Fee Collection Report",
+ "link_count": 0,
"link_to": "Program wise Fee Collection",
"link_type": "Report",
"onboard": 0,
@@ -344,6 +382,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Schedule",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -352,6 +391,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Course Schedule",
+ "link_count": 0,
"link_to": "Course Schedule",
"link_type": "DocType",
"onboard": 0,
@@ -362,6 +402,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Course Scheduling Tool",
+ "link_count": 0,
"link_to": "Course Scheduling Tool",
"link_type": "DocType",
"onboard": 0,
@@ -371,6 +412,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Attendance",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -379,6 +421,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Student Attendance",
+ "link_count": 0,
"link_to": "Student Attendance",
"link_type": "DocType",
"onboard": 0,
@@ -389,6 +432,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Student Leave Application",
+ "link_count": 0,
"link_to": "Student Leave Application",
"link_type": "DocType",
"onboard": 0,
@@ -399,6 +443,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Student Monthly Attendance Sheet",
+ "link_count": 0,
"link_to": "Student Monthly Attendance Sheet",
"link_type": "Report",
"onboard": 0,
@@ -409,6 +454,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Absent Student Report",
+ "link_count": 0,
"link_to": "Absent Student Report",
"link_type": "Report",
"onboard": 0,
@@ -419,6 +465,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Student Batch-Wise Attendance",
+ "link_count": 0,
"link_to": "Student Batch-Wise Attendance",
"link_type": "Report",
"onboard": 0,
@@ -428,6 +475,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "LMS Activity",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -436,6 +484,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Course Enrollment",
+ "link_count": 0,
"link_to": "Course Enrollment",
"link_type": "DocType",
"onboard": 0,
@@ -446,6 +495,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Course Activity",
+ "link_count": 0,
"link_to": "Course Activity",
"link_type": "DocType",
"onboard": 0,
@@ -456,6 +506,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Quiz Activity",
+ "link_count": 0,
"link_to": "Quiz Activity",
"link_type": "DocType",
"onboard": 0,
@@ -465,6 +516,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Assessment",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -473,6 +525,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Assessment Plan",
+ "link_count": 0,
"link_to": "Assessment Plan",
"link_type": "DocType",
"onboard": 0,
@@ -483,6 +536,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Assessment Group",
+ "link_count": 0,
"link_to": "Assessment Group",
"link_type": "DocType",
"onboard": 0,
@@ -493,6 +547,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Assessment Result",
+ "link_count": 0,
"link_to": "Assessment Result",
"link_type": "DocType",
"onboard": 0,
@@ -503,6 +558,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Assessment Criteria",
+ "link_count": 0,
"link_to": "Assessment Criteria",
"link_type": "DocType",
"onboard": 0,
@@ -512,6 +568,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Assessment Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -520,6 +577,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Course wise Assessment Report",
+ "link_count": 0,
"link_to": "Course wise Assessment Report",
"link_type": "Report",
"onboard": 0,
@@ -530,6 +588,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Final Assessment Grades",
+ "link_count": 0,
"link_to": "Final Assessment Grades",
"link_type": "Report",
"onboard": 0,
@@ -540,6 +599,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Assessment Plan Status",
+ "link_count": 0,
"link_to": "Assessment Plan Status",
"link_type": "Report",
"onboard": 0,
@@ -550,6 +610,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Student Report Generation Tool",
+ "link_count": 0,
"link_to": "Student Report Generation Tool",
"link_type": "DocType",
"onboard": 0,
@@ -559,6 +620,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Tools",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -567,6 +629,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Student Attendance Tool",
+ "link_count": 0,
"link_to": "Student Attendance Tool",
"link_type": "DocType",
"onboard": 0,
@@ -577,6 +640,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Assessment Result Tool",
+ "link_count": 0,
"link_to": "Assessment Result Tool",
"link_type": "DocType",
"onboard": 0,
@@ -587,6 +651,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Student Group Creation Tool",
+ "link_count": 0,
"link_to": "Student Group Creation Tool",
"link_type": "DocType",
"onboard": 0,
@@ -597,6 +662,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Program Enrollment Tool",
+ "link_count": 0,
"link_to": "Program Enrollment Tool",
"link_type": "DocType",
"onboard": 0,
@@ -607,6 +673,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Course Scheduling Tool",
+ "link_count": 0,
"link_to": "Course Scheduling Tool",
"link_type": "DocType",
"onboard": 0,
@@ -616,6 +683,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Other Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -624,21 +692,26 @@
"hidden": 0,
"is_query_report": 1,
"label": "Student and Guardian Contact Details",
+ "link_count": 0,
"link_to": "Student and Guardian Contact Details",
"link_type": "Report",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2020-12-01 13:38:37.448989",
+ "modified": "2021-08-05 12:15:57.929275",
"modified_by": "Administrator",
"module": "Education",
"name": "Education",
"onboarding": "Education",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
"restrict_to_domain": "Education",
+ "roles": [],
+ "sequence_id": 9,
"shortcuts": [
{
"color": "Grey",
@@ -697,5 +770,6 @@
"link_to": "Education",
"type": "Dashboard"
}
- ]
+ ],
+ "title": "Education"
}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py
deleted file mode 100644
index 5d5b2e1..0000000
--- a/erpnext/erpnext_integrations/connectors/shopify_connection.py
+++ /dev/null
@@ -1,353 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-import json
-from frappe.utils import cstr, cint, nowdate, getdate, flt, get_request_session, get_datetime
-from erpnext.erpnext_integrations.utils import validate_webhooks_request
-from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note, make_sales_invoice
-from erpnext.erpnext_integrations.doctype.shopify_settings.sync_product import sync_item_from_shopify
-from erpnext.erpnext_integrations.doctype.shopify_settings.sync_customer import create_customer
-from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log, dump_request_data
-from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import get_shopify_url, get_header
-
-@frappe.whitelist(allow_guest=True)
-@validate_webhooks_request("Shopify Settings", 'X-Shopify-Hmac-Sha256', secret_key='shared_secret')
-def store_request_data(order=None, event=None):
- if frappe.request:
- order = json.loads(frappe.request.data)
- event = frappe.request.headers.get('X-Shopify-Topic')
-
- dump_request_data(order, event)
-
-def sync_sales_order(order, request_id=None, old_order_sync=False):
- frappe.set_user('Administrator')
- shopify_settings = frappe.get_doc("Shopify Settings")
- frappe.flags.request_id = request_id
-
- if not frappe.db.get_value("Sales Order", filters={"shopify_order_id": cstr(order['id'])}):
- try:
- validate_customer(order, shopify_settings)
- validate_item(order, shopify_settings)
- create_order(order, shopify_settings, old_order_sync=old_order_sync)
- except Exception as e:
- make_shopify_log(status="Error", exception=e)
-
- else:
- make_shopify_log(status="Success")
-
-def prepare_sales_invoice(order, request_id=None):
- frappe.set_user('Administrator')
- shopify_settings = frappe.get_doc("Shopify Settings")
- frappe.flags.request_id = request_id
-
- try:
- sales_order = get_sales_order(cstr(order['id']))
- if sales_order:
- create_sales_invoice(order, shopify_settings, sales_order)
- make_shopify_log(status="Success")
- except Exception as e:
- make_shopify_log(status="Error", exception=e, rollback=True)
-
-def prepare_delivery_note(order, request_id=None):
- frappe.set_user('Administrator')
- shopify_settings = frappe.get_doc("Shopify Settings")
- frappe.flags.request_id = request_id
-
- try:
- sales_order = get_sales_order(cstr(order['id']))
- if sales_order:
- create_delivery_note(order, shopify_settings, sales_order)
- make_shopify_log(status="Success")
- except Exception as e:
- make_shopify_log(status="Error", exception=e, rollback=True)
-
-def get_sales_order(shopify_order_id):
- sales_order = frappe.db.get_value("Sales Order", filters={"shopify_order_id": shopify_order_id})
- if sales_order:
- so = frappe.get_doc("Sales Order", sales_order)
- return so
-
-def validate_customer(order, shopify_settings):
- customer_id = order.get("customer", {}).get("id")
- if customer_id:
- if not frappe.db.get_value("Customer", {"shopify_customer_id": customer_id}, "name"):
- create_customer(order.get("customer"), shopify_settings)
-
-def validate_item(order, shopify_settings):
- for item in order.get("line_items"):
- if item.get("product_id") and not frappe.db.get_value("Item", {"shopify_product_id": item.get("product_id")}, "name"):
- sync_item_from_shopify(shopify_settings, item)
-
-def create_order(order, shopify_settings, old_order_sync=False, company=None):
- so = create_sales_order(order, shopify_settings, company)
- if so:
- if order.get("financial_status") == "paid":
- create_sales_invoice(order, shopify_settings, so, old_order_sync=old_order_sync)
-
- if order.get("fulfillments") and not old_order_sync:
- create_delivery_note(order, shopify_settings, so)
-
-def create_sales_order(shopify_order, shopify_settings, company=None):
- product_not_exists = []
- customer = frappe.db.get_value("Customer", {"shopify_customer_id": shopify_order.get("customer", {}).get("id")}, "name")
- so = frappe.db.get_value("Sales Order", {"shopify_order_id": shopify_order.get("id")}, "name")
-
- if not so:
- items = get_order_items(shopify_order.get("line_items"), shopify_settings, getdate(shopify_order.get('created_at')))
-
- if not items:
- message = 'Following items exists in the shopify order but relevant records were not found in the shopify Product master'
- message += "\n" + ", ".join(product_not_exists)
-
- make_shopify_log(status="Error", exception=message, rollback=True)
-
- return ''
-
- so = frappe.get_doc({
- "doctype": "Sales Order",
- "naming_series": shopify_settings.sales_order_series or "SO-Shopify-",
- "shopify_order_id": shopify_order.get("id"),
- "shopify_order_number": shopify_order.get("name"),
- "customer": customer or shopify_settings.default_customer,
- "transaction_date": getdate(shopify_order.get("created_at")) or nowdate(),
- "delivery_date": getdate(shopify_order.get("created_at")) or nowdate(),
- "company": shopify_settings.company,
- "selling_price_list": shopify_settings.price_list,
- "ignore_pricing_rule": 1,
- "items": items,
- "taxes": get_order_taxes(shopify_order, shopify_settings),
- "apply_discount_on": "Grand Total",
- "discount_amount": get_discounted_amount(shopify_order),
- })
-
- if company:
- so.update({
- "company": company,
- "status": "Draft"
- })
- so.flags.ignore_mandatory = True
- so.save(ignore_permissions=True)
- so.submit()
-
- else:
- so = frappe.get_doc("Sales Order", so)
-
- frappe.db.commit()
- return so
-
-def create_sales_invoice(shopify_order, shopify_settings, so, old_order_sync=False):
- if not frappe.db.get_value("Sales Invoice", {"shopify_order_id": shopify_order.get("id")}, "name")\
- and so.docstatus==1 and not so.per_billed and cint(shopify_settings.sync_sales_invoice):
-
- if old_order_sync:
- posting_date = getdate(shopify_order.get('created_at'))
- else:
- posting_date = nowdate()
-
- si = make_sales_invoice(so.name, ignore_permissions=True)
- si.shopify_order_id = shopify_order.get("id")
- si.shopify_order_number = shopify_order.get("name")
- si.set_posting_time = 1
- si.posting_date = posting_date
- si.due_date = posting_date
- si.naming_series = shopify_settings.sales_invoice_series or "SI-Shopify-"
- si.flags.ignore_mandatory = True
- set_cost_center(si.items, shopify_settings.cost_center)
- si.insert(ignore_mandatory=True)
- si.submit()
- make_payament_entry_against_sales_invoice(si, shopify_settings, posting_date)
- frappe.db.commit()
-
-def set_cost_center(items, cost_center):
- for item in items:
- item.cost_center = cost_center
-
-def make_payament_entry_against_sales_invoice(doc, shopify_settings, posting_date=None):
- from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
- payment_entry = get_payment_entry(doc.doctype, doc.name, bank_account=shopify_settings.cash_bank_account)
- payment_entry.flags.ignore_mandatory = True
- payment_entry.reference_no = doc.name
- payment_entry.posting_date = posting_date or nowdate()
- payment_entry.reference_date = posting_date or nowdate()
- payment_entry.insert(ignore_permissions=True)
- payment_entry.submit()
-
-def create_delivery_note(shopify_order, shopify_settings, so):
- if not cint(shopify_settings.sync_delivery_note):
- return
-
- for fulfillment in shopify_order.get("fulfillments"):
- if not frappe.db.get_value("Delivery Note", {"shopify_fulfillment_id": fulfillment.get("id")}, "name")\
- and so.docstatus==1:
-
- dn = make_delivery_note(so.name)
- dn.shopify_order_id = fulfillment.get("order_id")
- dn.shopify_order_number = shopify_order.get("name")
- dn.set_posting_time = 1
- dn.posting_date = getdate(fulfillment.get("created_at"))
- dn.shopify_fulfillment_id = fulfillment.get("id")
- dn.naming_series = shopify_settings.delivery_note_series or "DN-Shopify-"
- dn.items = get_fulfillment_items(dn.items, fulfillment.get("line_items"), shopify_settings)
- dn.flags.ignore_mandatory = True
- dn.save()
- dn.submit()
- frappe.db.commit()
-
-def get_fulfillment_items(dn_items, fulfillment_items, shopify_settings):
- return [dn_item.update({"qty": item.get("quantity")}) for item in fulfillment_items for dn_item in dn_items\
- if get_item_code(item) == dn_item.item_code]
-
-def get_discounted_amount(order):
- discounted_amount = 0.0
- for discount in order.get("discount_codes"):
- discounted_amount += flt(discount.get("amount"))
- return discounted_amount
-
-def get_order_items(order_items, shopify_settings, delivery_date):
- items = []
- all_product_exists = True
- product_not_exists = []
-
- for shopify_item in order_items:
- if not shopify_item.get('product_exists'):
- all_product_exists = False
- product_not_exists.append({'title':shopify_item.get('title'),
- 'shopify_order_id': shopify_item.get('id')})
- continue
-
- if all_product_exists:
- item_code = get_item_code(shopify_item)
- items.append({
- "item_code": item_code,
- "item_name": shopify_item.get("name"),
- "rate": shopify_item.get("price"),
- "delivery_date": delivery_date,
- "qty": shopify_item.get("quantity"),
- "stock_uom": shopify_item.get("uom") or _("Nos"),
- "warehouse": shopify_settings.warehouse
- })
- else:
- items = []
-
- return items
-
-def get_item_code(shopify_item):
- item_code = frappe.db.get_value("Item", {"shopify_variant_id": shopify_item.get("variant_id")}, "item_code")
- if not item_code:
- item_code = frappe.db.get_value("Item", {"shopify_product_id": shopify_item.get("product_id")}, "item_code")
- if not item_code:
- item_code = frappe.db.get_value("Item", {"item_name": shopify_item.get("title")}, "item_code")
-
- return item_code
-
-def get_order_taxes(shopify_order, shopify_settings):
- taxes = []
- for tax in shopify_order.get("tax_lines"):
- taxes.append({
- "charge_type": _("On Net Total"),
- "account_head": get_tax_account_head(tax),
- "description": "{0} - {1}%".format(tax.get("title"), tax.get("rate") * 100.0),
- "rate": tax.get("rate") * 100.00,
- "included_in_print_rate": 1 if shopify_order.get("taxes_included") else 0,
- "cost_center": shopify_settings.cost_center
- })
-
- taxes = update_taxes_with_shipping_lines(taxes, shopify_order.get("shipping_lines"), shopify_settings)
-
- return taxes
-
-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:
- if shipping_charge.get("price"):
- taxes.append({
- "charge_type": _("Actual"),
- "account_head": get_tax_account_head(shipping_charge),
- "description": shipping_charge["title"],
- "tax_amount": shipping_charge["price"],
- "cost_center": shopify_settings.cost_center
- })
-
- for tax in shipping_charge.get("tax_lines"):
- taxes.append({
- "charge_type": _("Actual"),
- "account_head": get_tax_account_head(tax),
- "description": tax["title"],
- "tax_amount": tax["price"],
- "cost_center": shopify_settings.cost_center
- })
-
- return taxes
-
-def get_tax_account_head(tax):
- tax_title = tax.get("title").encode("utf-8")
-
- tax_account = frappe.db.get_value("Shopify Tax Account", \
- {"parent": "Shopify Settings", "shopify_tax": tax_title}, "tax_account")
-
- if not tax_account:
- frappe.throw(_("Tax Account not specified for Shopify Tax {0}").format(tax.get("title")))
-
- return tax_account
-
-@frappe.whitelist(allow_guest=True)
-def sync_old_orders():
- frappe.set_user('Administrator')
- shopify_settings = frappe.get_doc('Shopify Settings')
-
- if not shopify_settings.sync_missing_orders:
- return
-
- url = get_url(shopify_settings)
- session = get_request_session()
-
- try:
- res = session.get(url, headers=get_header(shopify_settings))
- res.raise_for_status()
- orders = res.json()["orders"]
-
- for order in orders:
- if is_sync_complete(shopify_settings, order):
- stop_sync(shopify_settings)
- return
-
- sync_sales_order(order=order, old_order_sync=True)
- last_order_id = order.get('id')
-
- if last_order_id:
- shopify_settings.load_from_db()
- shopify_settings.last_order_id = last_order_id
- shopify_settings.save()
- frappe.db.commit()
-
- except Exception as e:
- raise e
-
-def stop_sync(shopify_settings):
- shopify_settings.sync_missing_orders = 0
- shopify_settings.last_order_id = ''
- shopify_settings.save()
- frappe.db.commit()
-
-def get_url(shopify_settings):
- last_order_id = shopify_settings.last_order_id
-
- if not last_order_id:
- if shopify_settings.sync_based_on == 'Date':
- url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&created_at_min={0}&since_id=0".format(
- get_datetime(shopify_settings.from_date)), shopify_settings)
- else:
- url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&since_id={0}".format(
- shopify_settings.from_order_id), shopify_settings)
- else:
- url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&since_id={0}".format(last_order_id), shopify_settings)
-
- return url
-
-def is_sync_complete(shopify_settings, order):
- if shopify_settings.sync_based_on == 'Date':
- return getdate(shopify_settings.to_date) < getdate(order.get('created_at'))
- else:
- return cstr(order.get('id')) == cstr(shopify_settings.to_order_id)
-
diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.js b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.js
deleted file mode 100644
index d3fe7d2b..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.js
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Shopify Log', {
- refresh: function(frm) {
- if (frm.doc.request_data && frm.doc.status=='Error'){
- frm.add_custom_button('Resync', function() {
- frappe.call({
- method:"erpnext.erpnext_integrations.doctype.shopify_log.shopify_log.resync",
- args:{
- method:frm.doc.method,
- name: frm.doc.name,
- request_data: frm.doc.request_data
- },
- callback: function(r){
- frappe.msgprint(__("Order rescheduled for sync"))
- }
- })
- }).addClass('btn-primary');
- }
- }
-});
diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.json b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.json
deleted file mode 100644
index ab373ee..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.json
+++ /dev/null
@@ -1,268 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2016-03-14 10:02:06.227184",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "System",
- "editable_grid": 0,
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "title",
- "fieldtype": "Data",
- "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": "Title",
- "length": 0,
- "no_copy": 0,
- "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,
- "default": "Queued",
- "fieldname": "status",
- "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": "Status",
- "length": 0,
- "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,
- "fieldname": "method",
- "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": "Method",
- "length": 0,
- "no_copy": 0,
- "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,
- "fieldname": "message",
- "fieldtype": "Code",
- "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": "Message",
- "length": 0,
- "no_copy": 0,
- "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,
- "fieldname": "traceback",
- "fieldtype": "Code",
- "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": "Traceback",
- "length": 0,
- "no_copy": 0,
- "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,
- "fieldname": "request_data",
- "fieldtype": "Code",
- "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": "Request Data",
- "length": 0,
- "no_copy": 0,
- "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
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 1,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-04-20 16:23:36.862381",
- "modified_by": "Administrator",
- "module": "ERPNext Integrations",
- "name": "Shopify Log",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Administrator",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- },
- {
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "title",
- "track_changes": 0,
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.py b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.py
deleted file mode 100644
index a2b6af9..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-import json
-from frappe.model.document import Document
-from erpnext.erpnext_integrations.utils import get_webhook_address
-
-class ShopifyLog(Document):
- pass
-
-
-def make_shopify_log(status="Queued", exception=None, rollback=False):
- # if name not provided by log calling method then fetch existing queued state log
- make_new = False
-
- if not frappe.flags.request_id:
- make_new = True
-
- if rollback:
- frappe.db.rollback()
-
- if make_new:
- log = frappe.get_doc({"doctype":"Shopify Log"}).insert(ignore_permissions=True)
- else:
- log = log = frappe.get_doc("Shopify Log", frappe.flags.request_id)
-
- log.message = get_message(exception)
- log.traceback = frappe.get_traceback()
- log.status = status
- log.save(ignore_permissions=True)
- frappe.db.commit()
-
-def get_message(exception):
- message = None
-
- if hasattr(exception, 'message'):
- message = exception.message
- elif hasattr(exception, '__str__'):
- message = exception.__str__()
- else:
- message = "Something went wrong while syncing"
- return message
-
-def dump_request_data(data, event="create/order"):
- event_mapper = {
- "orders/create": get_webhook_address(connector_name='shopify_connection', method="sync_sales_order", exclude_uri=True),
- "orders/paid" : get_webhook_address(connector_name='shopify_connection', method="prepare_sales_invoice", exclude_uri=True),
- "orders/fulfilled": get_webhook_address(connector_name='shopify_connection', method="prepare_delivery_note", exclude_uri=True)
- }
-
- log = frappe.get_doc({
- "doctype": "Shopify Log",
- "request_data": json.dumps(data, indent=1),
- "method": event_mapper[event]
- }).insert(ignore_permissions=True)
-
- frappe.db.commit()
- frappe.enqueue(method=event_mapper[event], queue='short', timeout=300, is_async=True,
- **{"order": data, "request_id": log.name})
-
-@frappe.whitelist()
-def resync(method, name, request_data):
- frappe.db.set_value("Shopify Log", name, "status", "Queued", update_modified=False)
- frappe.enqueue(method=method, queue='short', timeout=300, is_async=True,
- **{"order": json.loads(request_data), "request_id": name})
diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log_list.js b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log_list.js
deleted file mode 100644
index 0913ce4..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log_list.js
+++ /dev/null
@@ -1,12 +0,0 @@
-frappe.listview_settings['Shopify Log'] = {
- add_fields: ["status"],
- get_indicator: function(doc) {
- if(doc.status==="Success"){
- return [__("Success"), "green", "status,=,Success"];
- } else if(doc.status ==="Error"){
- return [__("Error"), "red", "status,=,Error"];
- } else if(doc.status ==="Queued"){
- return [__("Queued"), "orange", "status,=,Queued"];
- }
- }
-}
diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.js b/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.js
deleted file mode 100644
index d22b6d5..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Shopify Log", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Shopify Log
- () => frappe.tests.make('Shopify Log', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.py b/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.py
deleted file mode 100644
index 5892e1d..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-
-# test_records = frappe.get_test_records('Shopify Log')
-
-class TestShopifyLog(unittest.TestCase):
- pass
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.js b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.js
deleted file mode 100644
index 1574795..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.js
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-// License: GNU General Public License v3. See license.txt
-
-frappe.provide("erpnext_integrations.shopify_settings");
-
-frappe.ui.form.on("Shopify Settings", "onload", function(frm){
- frappe.call({
- method:"erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings.get_series",
- callback:function(r){
- $.each(r.message, function(key, value){
- set_field_options(key, value);
- });
- }
- });
- erpnext_integrations.shopify_settings.setup_queries(frm);
-})
-
-frappe.ui.form.on("Shopify Settings", "app_type", function(frm) {
- frm.toggle_reqd("api_key", (frm.doc.app_type == "Private"));
- frm.toggle_reqd("password", (frm.doc.app_type == "Private"));
-})
-
-frappe.ui.form.on("Shopify Settings", "refresh", function(frm){
- if(!frm.doc.__islocal && frm.doc.enable_shopify === 1){
- frm.toggle_reqd("price_list", true);
- frm.toggle_reqd("warehouse", true);
- frm.toggle_reqd("taxes", true);
- frm.toggle_reqd("company", true);
- frm.toggle_reqd("cost_center", true);
- frm.toggle_reqd("cash_bank_account", true);
- frm.toggle_reqd("sales_order_series", true);
- frm.toggle_reqd("customer_group", true);
- frm.toggle_reqd("shared_secret", true);
-
- frm.toggle_reqd("sales_invoice_series", frm.doc.sync_sales_invoice);
- frm.toggle_reqd("delivery_note_series", frm.doc.sync_delivery_note);
-
- }
-})
-
-$.extend(erpnext_integrations.shopify_settings, {
- setup_queries: function(frm) {
- frm.fields_dict["warehouse"].get_query = function(doc) {
- return {
- filters:{
- "company": doc.company,
- "is_group": "No"
- }
- }
- }
-
- frm.fields_dict["taxes"].grid.get_field("tax_account").get_query = function(doc){
- return {
- "query": "erpnext.controllers.queries.tax_account_query",
- "filters": {
- "account_type": ["Tax", "Chargeable", "Expense Account"],
- "company": doc.company
- }
- }
- }
-
- frm.fields_dict["cash_bank_account"].get_query = function(doc) {
- return {
- filters: [
- ["Account", "account_type", "in", ["Cash", "Bank"]],
- ["Account", "root_type", "=", "Asset"],
- ["Account", "is_group", "=",0],
- ["Account", "company", "=", doc.company]
- ]
- }
- }
-
- frm.fields_dict["cost_center"].get_query = function(doc) {
- return {
- filters:{
- "company": doc.company,
- "is_group": "No"
- }
- }
- }
-
- frm.fields_dict["price_list"].get_query = function() {
- return {
- filters:{
- "selling": 1
- }
- }
- }
- }
-})
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json
deleted file mode 100644
index 308e7d1..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json
+++ /dev/null
@@ -1,353 +0,0 @@
-{
- "actions": [],
- "creation": "2015-05-18 05:21:07.270859",
- "doctype": "DocType",
- "document_type": "System",
- "engine": "InnoDB",
- "field_order": [
- "status_html",
- "enable_shopify",
- "app_type",
- "column_break_4",
- "last_sync_datetime",
- "section_break_2",
- "shopify_url",
- "api_key",
- "column_break_3",
- "password",
- "shared_secret",
- "access_token",
- "section_break_38",
- "webhooks",
- "section_break_15",
- "default_customer",
- "column_break_19",
- "customer_group",
- "company_dependent_settings",
- "company",
- "cash_bank_account",
- "column_break_20",
- "cost_center",
- "erp_settings",
- "price_list",
- "update_price_in_erpnext_price_list",
- "column_break_26",
- "warehouse",
- "section_break_25",
- "sales_order_series",
- "column_break_27",
- "sync_delivery_note",
- "delivery_note_series",
- "sync_sales_invoice",
- "sales_invoice_series",
- "section_break_22",
- "html_16",
- "taxes",
- "syncing_details_section",
- "sync_missing_orders",
- "sync_based_on",
- "column_break_41",
- "from_date",
- "to_date",
- "from_order_id",
- "to_order_id",
- "last_order_id"
- ],
- "fields": [
- {
- "fieldname": "status_html",
- "fieldtype": "HTML",
- "label": "status html",
- "read_only": 1
- },
- {
- "default": "0",
- "fieldname": "enable_shopify",
- "fieldtype": "Check",
- "label": "Enable Shopify"
- },
- {
- "default": "Private",
- "fieldname": "app_type",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "App Type",
- "read_only": 1,
- "reqd": 1
- },
- {
- "fieldname": "column_break_4",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "last_sync_datetime",
- "fieldtype": "Datetime",
- "label": "Last Sync Datetime",
- "read_only": 1
- },
- {
- "fieldname": "section_break_2",
- "fieldtype": "Section Break"
- },
- {
- "description": "eg: frappe.myshopify.com",
- "fieldname": "shopify_url",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Shop URL",
- "reqd": 1
- },
- {
- "depends_on": "eval:doc.app_type==\"Private\"",
- "fieldname": "api_key",
- "fieldtype": "Data",
- "label": "API Key"
- },
- {
- "fieldname": "column_break_3",
- "fieldtype": "Column Break"
- },
- {
- "depends_on": "eval:doc.app_type==\"Private\"",
- "fieldname": "password",
- "fieldtype": "Password",
- "label": "Password"
- },
- {
- "fieldname": "shared_secret",
- "fieldtype": "Data",
- "label": "Shared secret"
- },
- {
- "fieldname": "access_token",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Access Token",
- "read_only": 1
- },
- {
- "collapsible": 1,
- "fieldname": "section_break_38",
- "fieldtype": "Section Break",
- "label": "Webhooks Details"
- },
- {
- "fieldname": "webhooks",
- "fieldtype": "Table",
- "label": "Webhooks",
- "options": "Shopify Webhook Detail",
- "read_only": 1
- },
- {
- "fieldname": "section_break_15",
- "fieldtype": "Section Break",
- "label": "Customer Settings"
- },
- {
- "description": "If Shopify does not have a customer in the order, then while syncing the orders, the system will consider the default customer for the order",
- "fieldname": "default_customer",
- "fieldtype": "Link",
- "label": "Default Customer",
- "options": "Customer"
- },
- {
- "fieldname": "column_break_19",
- "fieldtype": "Column Break"
- },
- {
- "description": "Customer Group will set to selected group while syncing customers from Shopify",
- "fieldname": "customer_group",
- "fieldtype": "Link",
- "label": "Customer Group",
- "options": "Customer Group"
- },
- {
- "fieldname": "company_dependent_settings",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "label": "For Company",
- "options": "Company"
- },
- {
- "description": "Cash Account will used for Sales Invoice creation",
- "fieldname": "cash_bank_account",
- "fieldtype": "Link",
- "label": "Cash/Bank Account",
- "options": "Account"
- },
- {
- "fieldname": "column_break_20",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "cost_center",
- "fieldtype": "Link",
- "label": "Cost Center",
- "options": "Cost Center"
- },
- {
- "fieldname": "erp_settings",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "price_list",
- "fieldtype": "Link",
- "label": "Price List",
- "options": "Price List"
- },
- {
- "default": "0",
- "fieldname": "update_price_in_erpnext_price_list",
- "fieldtype": "Check",
- "label": "Update Price from Shopify To ERPNext Price List"
- },
- {
- "fieldname": "column_break_26",
- "fieldtype": "Column Break"
- },
- {
- "description": "Default Warehouse to to create Sales Order and Delivery Note",
- "fieldname": "warehouse",
- "fieldtype": "Link",
- "label": "Warehouse",
- "options": "Warehouse"
- },
- {
- "fieldname": "section_break_25",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "sales_order_series",
- "fieldtype": "Select",
- "label": "Sales Order Series"
- },
- {
- "fieldname": "column_break_27",
- "fieldtype": "Column Break"
- },
- {
- "default": "0",
- "fieldname": "sync_delivery_note",
- "fieldtype": "Check",
- "label": "Import Delivery Notes from Shopify on Shipment"
- },
- {
- "depends_on": "eval:doc.sync_delivery_note==1",
- "fieldname": "delivery_note_series",
- "fieldtype": "Select",
- "label": "Delivery Note Series"
- },
- {
- "default": "0",
- "fieldname": "sync_sales_invoice",
- "fieldtype": "Check",
- "label": "Import Sales Invoice from Shopify if Payment is marked"
- },
- {
- "depends_on": "eval:doc.sync_sales_invoice==1",
- "fieldname": "sales_invoice_series",
- "fieldtype": "Select",
- "label": "Sales Invoice Series"
- },
- {
- "fieldname": "section_break_22",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "html_16",
- "fieldtype": "HTML",
- "options": "Map Shopify Taxes / Shipping Charges to ERPNext Account"
- },
- {
- "fieldname": "taxes",
- "fieldtype": "Table",
- "label": "Shopify Tax Account",
- "options": "Shopify Tax Account"
- },
- {
- "collapsible": 1,
- "fieldname": "syncing_details_section",
- "fieldtype": "Section Break",
- "label": "Syncing Missing Orders"
- },
- {
- "depends_on": "eval:doc.sync_missing_orders",
- "fieldname": "last_order_id",
- "fieldtype": "Data",
- "label": "Last Order Id",
- "read_only": 1
- },
- {
- "fieldname": "column_break_41",
- "fieldtype": "Column Break"
- },
- {
- "default": "0",
- "description": "On checking this Order from the ",
- "fieldname": "sync_missing_orders",
- "fieldtype": "Check",
- "label": "Sync Missing Old Shopify Orders"
- },
- {
- "depends_on": "eval:doc.sync_missing_orders",
- "fieldname": "sync_based_on",
- "fieldtype": "Select",
- "label": "Sync Based On",
- "mandatory_depends_on": "eval:doc.sync_missing_orders",
- "options": "\nDate\nShopify Order Id"
- },
- {
- "depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders",
- "fieldname": "from_date",
- "fieldtype": "Date",
- "label": "From Date",
- "mandatory_depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders"
- },
- {
- "depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders",
- "fieldname": "to_date",
- "fieldtype": "Date",
- "label": "To Date",
- "mandatory_depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders"
- },
- {
- "depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders",
- "fieldname": "from_order_id",
- "fieldtype": "Data",
- "label": "From Order Id",
- "mandatory_depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders"
- },
- {
- "depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders",
- "fieldname": "to_order_id",
- "fieldtype": "Data",
- "label": "To Order Id",
- "mandatory_depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders"
- }
- ],
- "issingle": 1,
- "links": [],
- "modified": "2021-03-02 17:35:41.953317",
- "modified_by": "Administrator",
- "module": "ERPNext Integrations",
- "name": "Shopify Settings",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "print": 1,
- "read": 1,
- "role": "System Manager",
- "share": 1,
- "write": 1
- }
- ],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
deleted file mode 100644
index 381c5e5..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
+++ /dev/null
@@ -1,144 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-import json
-from frappe import _
-from frappe.model.document import Document
-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 erpnext.erpnext_integrations.utils import get_webhook_address
-from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log
-
-class ShopifySettings(Document):
- def validate(self):
- if self.enable_shopify == 1:
- setup_custom_fields()
- self.validate_access_credentials()
- self.register_webhooks()
- else:
- self.unregister_webhooks()
-
- def validate_access_credentials(self):
- if not (self.get_password(raise_exception=False) and self.api_key and self.shopify_url):
- frappe.msgprint(_("Missing value for Password, API Key or Shopify URL"), raise_exception=frappe.ValidationError)
-
- def register_webhooks(self):
- webhooks = ["orders/create", "orders/paid", "orders/fulfilled"]
- # url = get_shopify_url('admin/webhooks.json', self)
- created_webhooks = [d.method for d in self.webhooks]
- url = get_shopify_url('admin/api/2021-04/webhooks.json', self)
- for method in webhooks:
- session = get_request_session()
- try:
- res = session.post(url, data=json.dumps({
- "webhook": {
- "topic": method,
- "address": get_webhook_address(connector_name='shopify_connection', method='store_request_data', force_https=True),
- "format": "json"
- }
- }), headers=get_header(self))
- res.raise_for_status()
- 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:
- make_shopify_log(status="Warning", exception=e, rollback=True)
-
- def unregister_webhooks(self):
- session = get_request_session()
- deleted_webhooks = []
-
- for d in self.webhooks:
- url = get_shopify_url('admin/api/2021-04/webhooks/{0}.json'.format(d.webhook_id), self)
- try:
- res = session.delete(url, headers=get_header(self))
- res.raise_for_status()
- 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:
- frappe.log_error(message=e, title='Shopify Webhooks Issue')
-
- for d in deleted_webhooks:
- self.remove(d)
-
- def update_webhook_table(self, method, res):
- self.append("webhooks", {
- "webhook_id": res['webhook']['id'],
- "method": method
- })
-
-def get_shopify_url(path, settings):
- if settings.app_type == "Private":
- return 'https://{}:{}@{}/{}'.format(settings.api_key, settings.get_password('password'), settings.shopify_url, path)
- else:
- return 'https://{}/{}'.format(settings.shopify_url, path)
-
-def get_header(settings):
- header = {'Content-Type': 'application/json'}
-
- return header
-
-@frappe.whitelist()
-def get_series():
- return {
- "sales_order_series" : frappe.get_meta("Sales Order").get_options("naming_series") or "SO-Shopify-",
- "sales_invoice_series" : frappe.get_meta("Sales Invoice").get_options("naming_series") or "SI-Shopify-",
- "delivery_note_series" : frappe.get_meta("Delivery Note").get_options("naming_series") or "DN-Shopify-"
- }
-
-def setup_custom_fields():
- custom_fields = {
- "Customer": [
- dict(fieldname='shopify_customer_id', label='Shopify Customer Id',
- fieldtype='Data', insert_after='series', read_only=1, print_hide=1)
- ],
- "Supplier": [
- dict(fieldname='shopify_supplier_id', label='Shopify Supplier Id',
- fieldtype='Data', insert_after='supplier_name', read_only=1, print_hide=1)
- ],
- "Address": [
- dict(fieldname='shopify_address_id', label='Shopify Address Id',
- fieldtype='Data', insert_after='fax', read_only=1, print_hide=1)
- ],
- "Item": [
- dict(fieldname='shopify_variant_id', label='Shopify Variant Id',
- fieldtype='Data', insert_after='item_code', read_only=1, print_hide=1),
- dict(fieldname='shopify_product_id', label='Shopify Product Id',
- fieldtype='Data', insert_after='item_code', read_only=1, print_hide=1),
- dict(fieldname='shopify_description', label='Shopify Description',
- fieldtype='Text Editor', insert_after='description', read_only=1, print_hide=1)
- ],
- "Sales Order": [
- dict(fieldname='shopify_order_id', label='Shopify Order Id',
- fieldtype='Data', insert_after='title', read_only=1, print_hide=1),
- dict(fieldname='shopify_order_number', label='Shopify Order Number',
- fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1)
- ],
- "Delivery Note":[
- dict(fieldname='shopify_order_id', label='Shopify Order Id',
- fieldtype='Data', insert_after='title', read_only=1, print_hide=1),
- dict(fieldname='shopify_order_number', label='Shopify Order Number',
- fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1),
- dict(fieldname='shopify_fulfillment_id', label='Shopify Fulfillment Id',
- fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
- ],
- "Sales Invoice": [
- dict(fieldname='shopify_order_id', label='Shopify Order Id',
- fieldtype='Data', insert_after='title', read_only=1, print_hide=1),
- dict(fieldname='shopify_order_number', label='Shopify Order Number',
- fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1)
- ]
- }
-
- create_custom_fields(custom_fields)
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py
deleted file mode 100644
index 2af57f4..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py
+++ /dev/null
@@ -1,71 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-
-def create_customer(shopify_customer, shopify_settings):
- import frappe.utils.nestedset
-
- cust_name = (shopify_customer.get("first_name") + " " + (shopify_customer.get("last_name") \
- and shopify_customer.get("last_name") or "")) if shopify_customer.get("first_name")\
- else shopify_customer.get("email")
-
- try:
- customer = frappe.get_doc({
- "doctype": "Customer",
- "name": shopify_customer.get("id"),
- "customer_name" : cust_name,
- "shopify_customer_id": shopify_customer.get("id"),
- "sync_with_shopify": 1,
- "customer_group": shopify_settings.customer_group,
- "territory": frappe.utils.nestedset.get_root_of("Territory"),
- "customer_type": _("Individual")
- })
- customer.flags.ignore_mandatory = True
- customer.insert(ignore_permissions=True)
-
- if customer:
- create_customer_address(customer, shopify_customer)
-
- frappe.db.commit()
-
- except Exception as e:
- raise e
-
-def create_customer_address(customer, shopify_customer):
- addresses = shopify_customer.get("addresses", [])
-
- if not addresses and "default_address" in shopify_customer:
- addresses.append(shopify_customer["default_address"])
-
- for i, address in enumerate(addresses):
- address_title, address_type = get_address_title_and_type(customer.customer_name, i)
- try :
- frappe.get_doc({
- "doctype": "Address",
- "shopify_address_id": address.get("id"),
- "address_title": address_title,
- "address_type": address_type,
- "address_line1": address.get("address1") or "Address 1",
- "address_line2": address.get("address2"),
- "city": address.get("city") or "City",
- "state": address.get("province"),
- "pincode": address.get("zip"),
- "country": address.get("country"),
- "phone": address.get("phone"),
- "email_id": shopify_customer.get("email"),
- "links": [{
- "link_doctype": "Customer",
- "link_name": customer.name
- }]
- }).insert(ignore_mandatory=True)
-
- except Exception as e:
- raise e
-
-def get_address_title_and_type(customer_name, index):
- address_type = _("Billing")
- address_title = customer_name
- if frappe.db.get_value("Address", "{0}-{1}".format(customer_name.strip(), address_type)):
- address_title = "{0}-{1}".format(customer_name.strip(), index)
-
- return address_title, address_type
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py
deleted file mode 100644
index 16efb6c..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py
+++ /dev/null
@@ -1,309 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-from erpnext import get_default_company
-from frappe.utils import cstr, cint, get_request_session
-from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import get_shopify_url, get_header
-
-shopify_variants_attr_list = ["option1", "option2", "option3"]
-
-def sync_item_from_shopify(shopify_settings, item):
- url = get_shopify_url("admin/api/2021-04/products/{0}.json".format(item.get("product_id")), shopify_settings)
- session = get_request_session()
-
- try:
- res = session.get(url, headers=get_header(shopify_settings))
- res.raise_for_status()
-
- shopify_item = res.json()["product"]
- make_item(shopify_settings.warehouse, shopify_item)
- except Exception as e:
- raise e
-
-def make_item(warehouse, shopify_item):
- add_item_weight(shopify_item)
-
- if has_variants(shopify_item):
- attributes = create_attribute(shopify_item)
- create_item(shopify_item, warehouse, 1, attributes)
- create_item_variants(shopify_item, warehouse, attributes, shopify_variants_attr_list)
-
- else:
- shopify_item["variant_id"] = shopify_item['variants'][0]["id"]
- create_item(shopify_item, warehouse)
-
-def add_item_weight(shopify_item):
- shopify_item["weight"] = shopify_item['variants'][0]["weight"]
- shopify_item["weight_unit"] = shopify_item['variants'][0]["weight_unit"]
-
-def has_variants(shopify_item):
- if len(shopify_item.get("options")) >= 1 and "Default Title" not in shopify_item.get("options")[0]["values"]:
- return True
- return False
-
-def create_attribute(shopify_item):
- attribute = []
- # shopify item dict
- for attr in shopify_item.get('options'):
- if not frappe.db.get_value("Item Attribute", attr.get("name"), "name"):
- frappe.get_doc({
- "doctype": "Item Attribute",
- "attribute_name": attr.get("name"),
- "item_attribute_values": [
- {
- "attribute_value": attr_value,
- "abbr":attr_value
- }
- for attr_value in attr.get("values")
- ]
- }).insert()
- attribute.append({"attribute": attr.get("name")})
-
- else:
- # check for attribute values
- item_attr = frappe.get_doc("Item Attribute", attr.get("name"))
- if not item_attr.numeric_values:
- set_new_attribute_values(item_attr, attr.get("values"))
- item_attr.save()
- attribute.append({"attribute": attr.get("name")})
-
- else:
- attribute.append({
- "attribute": attr.get("name"),
- "from_range": item_attr.get("from_range"),
- "to_range": item_attr.get("to_range"),
- "increment": item_attr.get("increment"),
- "numeric_values": item_attr.get("numeric_values")
- })
-
- return attribute
-
-def set_new_attribute_values(item_attr, values):
- for attr_value in values:
- if not any((d.abbr.lower() == attr_value.lower() or d.attribute_value.lower() == attr_value.lower())\
- for d in item_attr.item_attribute_values):
- item_attr.append("item_attribute_values", {
- "attribute_value": attr_value,
- "abbr": attr_value
- })
-
-def create_item(shopify_item, warehouse, has_variant=0, attributes=None,variant_of=None):
- item_dict = {
- "doctype": "Item",
- "shopify_product_id": shopify_item.get("id"),
- "shopify_variant_id": shopify_item.get("variant_id"),
- "variant_of": variant_of,
- "sync_with_shopify": 1,
- "is_stock_item": 1,
- "item_code": cstr(shopify_item.get("item_code")) or cstr(shopify_item.get("id")),
- "item_name": shopify_item.get("title", '').strip(),
- "description": shopify_item.get("body_html") or shopify_item.get("title"),
- "shopify_description": shopify_item.get("body_html") or shopify_item.get("title"),
- "item_group": get_item_group(shopify_item.get("product_type")),
- "has_variants": has_variant,
- "attributes":attributes or [],
- "stock_uom": shopify_item.get("uom") or _("Nos"),
- "stock_keeping_unit": shopify_item.get("sku") or get_sku(shopify_item),
- "default_warehouse": warehouse,
- "image": get_item_image(shopify_item),
- "weight_uom": shopify_item.get("weight_unit"),
- "weight_per_unit": shopify_item.get("weight"),
- "default_supplier": get_supplier(shopify_item),
- "item_defaults": [
- {
- "company": get_default_company()
- }
- ]
- }
-
- if not is_item_exists(item_dict, attributes, variant_of=variant_of):
- item_details = get_item_details(shopify_item)
- name = ''
-
- if not item_details:
- new_item = frappe.get_doc(item_dict)
- new_item.insert(ignore_permissions=True, ignore_mandatory=True)
- name = new_item.name
-
- if not name:
- name = item_details.name
-
- if not has_variant:
- add_to_price_list(shopify_item, name)
-
- frappe.db.commit()
-
-def create_item_variants(shopify_item, warehouse, attributes, shopify_variants_attr_list):
- template_item = frappe.db.get_value("Item", filters={"shopify_product_id": shopify_item.get("id")},
- fieldname=["name", "stock_uom"], as_dict=True)
-
- if template_item:
- for variant in shopify_item.get("variants"):
- shopify_item_variant = {
- "id" : variant.get("id"),
- "item_code": variant.get("id"),
- "title": variant.get("title"),
- "product_type": shopify_item.get("product_type"),
- "sku": variant.get("sku"),
- "uom": template_item.stock_uom or _("Nos"),
- "item_price": variant.get("price"),
- "variant_id": variant.get("id"),
- "weight_unit": variant.get("weight_unit"),
- "weight": variant.get("weight")
- }
-
- for i, variant_attr in enumerate(shopify_variants_attr_list):
- if variant.get(variant_attr):
- attributes[i].update({"attribute_value": get_attribute_value(variant.get(variant_attr), attributes[i])})
- create_item(shopify_item_variant, warehouse, 0, attributes, template_item.name)
-
-def get_attribute_value(variant_attr_val, attribute):
- attribute_value = frappe.db.sql("""select attribute_value from `tabItem Attribute Value`
- where parent = %s and (abbr = %s or attribute_value = %s)""", (attribute["attribute"], variant_attr_val,
- variant_attr_val), as_list=1)
- return attribute_value[0][0] if len(attribute_value)>0 else cint(variant_attr_val)
-
-def get_item_group(product_type=None):
- import frappe.utils.nestedset
- parent_item_group = frappe.utils.nestedset.get_root_of("Item Group")
-
- if product_type:
- if not frappe.db.get_value("Item Group", product_type, "name"):
- item_group = frappe.get_doc({
- "doctype": "Item Group",
- "item_group_name": product_type,
- "parent_item_group": parent_item_group,
- "is_group": "No"
- }).insert()
- return item_group.name
- else:
- return product_type
- else:
- return parent_item_group
-
-
-def get_sku(item):
- if item.get("variants"):
- return item.get("variants")[0].get("sku")
- return ""
-
-def add_to_price_list(item, name):
- shopify_settings = frappe.db.get_value("Shopify Settings", None, ["price_list", "update_price_in_erpnext_price_list"], as_dict=1)
- if not shopify_settings.update_price_in_erpnext_price_list:
- return
-
- item_price_name = frappe.db.get_value("Item Price",
- {"item_code": name, "price_list": shopify_settings.price_list}, "name")
-
- if not item_price_name:
- frappe.get_doc({
- "doctype": "Item Price",
- "price_list": shopify_settings.price_list,
- "item_code": name,
- "price_list_rate": item.get("item_price") or item.get("variants")[0].get("price")
- }).insert()
- else:
- item_rate = frappe.get_doc("Item Price", item_price_name)
- item_rate.price_list_rate = item.get("item_price") or item.get("variants")[0].get("price")
- item_rate.save()
-
-def get_item_image(shopify_item):
- if shopify_item.get("image"):
- return shopify_item.get("image").get("src")
- return None
-
-def get_supplier(shopify_item):
- if shopify_item.get("vendor"):
- supplier = frappe.db.sql("""select name from tabSupplier
- where name = %s or shopify_supplier_id = %s """, (shopify_item.get("vendor"),
- shopify_item.get("vendor").lower()), as_list=1)
-
- if not supplier:
- supplier = frappe.get_doc({
- "doctype": "Supplier",
- "supplier_name": shopify_item.get("vendor"),
- "shopify_supplier_id": shopify_item.get("vendor").lower(),
- "supplier_group": get_supplier_group()
- }).insert()
- return supplier.name
- else:
- return shopify_item.get("vendor")
- else:
- return ""
-
-def get_supplier_group():
- supplier_group = frappe.db.get_value("Supplier Group", _("Shopify Supplier"))
- if not supplier_group:
- supplier_group = frappe.get_doc({
- "doctype": "Supplier Group",
- "supplier_group_name": _("Shopify Supplier")
- }).insert()
- return supplier_group.name
- return supplier_group
-
-def get_item_details(shopify_item):
- item_details = {}
-
- item_details = frappe.db.get_value("Item", {"shopify_product_id": shopify_item.get("id")},
- ["name", "stock_uom", "item_name"], as_dict=1)
-
- if item_details:
- return item_details
-
- else:
- item_details = frappe.db.get_value("Item", {"shopify_variant_id": shopify_item.get("id")},
- ["name", "stock_uom", "item_name"], as_dict=1)
- return item_details
-
-def is_item_exists(shopify_item, attributes=None, variant_of=None):
- if variant_of:
- name = variant_of
- else:
- name = frappe.db.get_value("Item", {"item_name": shopify_item.get("item_name")})
-
- if name:
- item = frappe.get_doc("Item", name)
- item.flags.ignore_mandatory=True
-
- if not variant_of and not item.shopify_product_id:
- item.shopify_product_id = shopify_item.get("shopify_product_id")
- item.shopify_variant_id = shopify_item.get("shopify_variant_id")
- item.save()
- return True
-
- if item.shopify_product_id and attributes and attributes[0].get("attribute_value"):
- if not variant_of:
- variant_of = frappe.db.get_value("Item",
- {"shopify_product_id": item.shopify_product_id}, "variant_of")
-
- # create conditions for all item attributes,
- # as we are putting condition basis on OR it will fetch all items matching either of conditions
- # thus comparing matching conditions with len(attributes)
- # which will give exact matching variant item.
-
- conditions = ["(iv.attribute='{0}' and iv.attribute_value = '{1}')"\
- .format(attr.get("attribute"), attr.get("attribute_value")) for attr in attributes]
-
- conditions = "( {0} ) and iv.parent = it.name ) = {1}".format(" or ".join(conditions), len(attributes))
-
- parent = frappe.db.sql(""" select * from tabItem it where
- ( select count(*) from `tabItem Variant Attribute` iv
- where {conditions} and it.variant_of = %s """.format(conditions=conditions) ,
- variant_of, as_list=1)
-
- if parent:
- variant = frappe.get_doc("Item", parent[0][0])
- variant.flags.ignore_mandatory = True
-
- variant.shopify_product_id = shopify_item.get("shopify_product_id")
- variant.shopify_variant_id = shopify_item.get("shopify_variant_id")
- variant.save()
- return False
-
- if item.shopify_product_id and item.shopify_product_id != shopify_item.get("shopify_product_id"):
- return False
-
- return True
-
- else:
- return False
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json b/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json
deleted file mode 100644
index db6c3d5..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json
+++ /dev/null
@@ -1,527 +0,0 @@
-[
- {
- "allow_on_submit": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "doctype": "Custom Field",
- "dt": "Print Settings",
- "fieldname": "compact_item_print",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "insert_after": "with_letterhead",
- "label": "Compact Item Print",
- "modified": "2016-06-06 15:18:17.025602",
- "name": "Print Settings-compact_item_print",
- "no_copy": 0,
- "options": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "unique": 0,
- "width": null
- },
- {
- "allow_on_submit": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "doctype": "Custom Field",
- "dt": "Customer",
- "fieldname": "shopify_customer_id",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "insert_after": "naming_series",
- "label": "Shopify Customer Id",
- "modified": "2016-01-15 17:25:28.991818",
- "name": "Customer-shopify_customer_id",
- "no_copy": 1,
- "options": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "unique": 0,
- "width": null
- },
- {
- "allow_on_submit": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "doctype": "Custom Field",
- "dt": "Address",
- "fieldname": "shopify_address_id",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "insert_after": "fax",
- "label": "Shopify Address Id",
- "modified": "2016-01-15 17:50:52.213743",
- "name": "Address-shopify_address_id",
- "no_copy": 1,
- "options": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "unique": 0,
- "width": null
- },
- {
- "allow_on_submit": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "doctype": "Custom Field",
- "dt": "Sales Order",
- "fieldname": "shopify_order_id",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "insert_after": "title",
- "label": "Shopify Order Id",
- "modified": "2016-01-18 09:55:50.764524",
- "name": "Sales Order-shopify_order_id",
- "no_copy": 1,
- "options": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "unique": 0,
- "width": null
- },
- {
- "allow_on_submit": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "doctype": "Custom Field",
- "dt": "Item",
- "fieldname": "shopify_product_id",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "insert_after": "item_code",
- "label": "Shopify Product Id",
- "modified": "2016-01-19 15:44:16.132952",
- "name": "Item-shopify_product_id",
- "no_copy": 1,
- "options": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "unique": 0,
- "width": null
- },
- {
- "allow_on_submit": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "doctype": "Custom Field",
- "dt": "Sales Invoice",
- "fieldname": "shopify_order_id",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "insert_after": "naming_series",
- "label": "Shopify Order Id",
- "modified": "2016-01-19 16:30:12.261797",
- "name": "Sales Invoice-shopify_order_id",
- "no_copy": 1,
- "options": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "unique": 0,
- "width": null
- },
- {
- "allow_on_submit": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "doctype": "Custom Field",
- "dt": "Delivery Note",
- "fieldname": "shopify_order_id",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "insert_after": "title",
- "label": "Shopify Order Id",
- "modified": "2016-01-19 16:30:31.201198",
- "name": "Delivery Note-shopify_order_id",
- "no_copy": 1,
- "options": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "unique": 0,
- "width": null
- },
- {
- "allow_on_submit": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "doctype": "Custom Field",
- "dt": "Item",
- "fieldname": "stock_keeping_unit",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "insert_after": "stock_uom",
- "label": "Stock Keeping Unit",
- "modified": "2015-11-10 09:29:10.854943",
- "name": "Item-stock_keeping_unit",
- "no_copy": 1,
- "options": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "unique": 0,
- "width": null
- },
- {
- "allow_on_submit": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "default": "0",
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "doctype": "Custom Field",
- "dt": "Item",
- "fieldname": "sync_with_shopify",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "insert_after": "is_stock_item",
- "label": "Sync With Shopify",
- "modified": "2015-10-12 15:54:31.997714",
- "name": "Item-sync_with_shopify",
- "no_copy": 0,
- "options": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "unique": 0,
- "width": null
- },
- {
- "allow_on_submit": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "doctype": "Custom Field",
- "dt": "Customer",
- "fieldname": "sync_with_shopify",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "insert_after": "is_frozen",
- "label": "Sync With Shopify",
- "modified": "2015-10-01 17:31:55.758826",
- "name": "Customer-sync_with_shopify",
- "no_copy": 0,
- "options": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "unique": 0,
- "width": null
- },
- {
- "allow_on_submit": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "doctype": "Custom Field",
- "dt": "Item",
- "fieldname": "shopify_variant_id",
- "fieldtype": "Data",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "insert_after": "item_code",
- "label": "Variant Id",
- "modified": "2015-11-09 18:26:50.825858",
- "name": "Item-shopify_variant_id",
- "no_copy": 1,
- "options": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "unique": 0,
- "width": null
- },
- {
- "allow_on_submit": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "doctype": "Custom Field",
- "dt": "Item",
- "fieldname": "sync_qty_with_shopify",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "insert_after": "item_code",
- "label": "Sync Quantity With Shopify",
- "modified": "2015-12-29 08:37:46.183295",
- "name": "Item-sync_qty_with_shopify",
- "no_copy": 0,
- "options": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "unique": 0,
- "width": null
- },
- {
- "allow_on_submit": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "doctype": "Custom Field",
- "dt": "Delivery Note",
- "fieldname": "shopify_fulfillment_id",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "insert_after": "title",
- "label": "Shopify Fulfillment Id",
- "modified": "2016-01-20 23:50:35.609543",
- "name": "Delivery Note-shopify_fulfillment_id",
- "no_copy": 1,
- "options": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "unique": 0,
- "width": null
- },
- {
- "allow_on_submit": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "doctype": "Custom Field",
- "dt": "Supplier",
- "fieldname": "shopify_supplier_id",
- "fieldtype": "Data",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "insert_after": "supplier_name",
- "label": "Shopify Supplier Id",
- "modified": "2016-02-01 15:41:25.818306",
- "name": "Supplier-shopify_supplier_id",
- "no_copy": 1,
- "options": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "unique": 0,
- "width": null
- },
- {
- "allow_on_submit": 0,
- "collapsible": 0,
- "collapsible_depends_on": null,
- "default": null,
- "depends_on": null,
- "description": null,
- "docstatus": 0,
- "doctype": "Custom Field",
- "dt": "Item",
- "fieldname": "shopify_description",
- "fieldtype": "Text Editor",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "insert_after": "section_break_11",
- "label": "shopify_description",
- "modified": "2016-06-15 12:15:36.325581",
- "name": "Item-shopify_description",
- "no_copy": 0,
- "options": null,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "print_width": null,
- "read_only": 0,
- "report_hide": 1,
- "reqd": 0,
- "search_index": 0,
- "unique": 0,
- "width": null
- }
-]
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_customer.json b/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_customer.json
deleted file mode 100644
index e91ce9a..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_customer.json
+++ /dev/null
@@ -1,59 +0,0 @@
-{
- "customer": {
- "id": 2324518599,
- "email": "andrew@wyatt.co.in",
- "accepts_marketing": false,
- "created_at": "2016-01-20T17:18:35+05:30",
- "updated_at": "2016-01-20T17:22:23+05:30",
- "first_name": "Andrew",
- "last_name": "Wyatt",
- "orders_count": 0,
- "state": "disabled",
- "total_spent": "0.00",
- "last_order_id": null,
- "note": "",
- "verified_email": true,
- "multipass_identifier": null,
- "tax_exempt": false,
- "tags": "",
- "last_order_name": null,
- "default_address": {
- "id": 2476804295,
- "first_name": "Andrew",
- "last_name": "Wyatt",
- "company": "Wyatt Inc.",
- "address1": "B-11, Betahouse",
- "address2": "Street 11, Sector 52",
- "city": "Manhattan",
- "province": "New York",
- "country": "United States",
- "zip": "10027",
- "phone": "145-112211",
- "name": "Andrew Wyatt",
- "province_code": "NY",
- "country_code": "US",
- "country_name": "United States",
- "default": true
- },
- "addresses": [
- {
- "id": 2476804295,
- "first_name": "Andrew",
- "last_name": "Wyatt",
- "company": "Wyatt Inc.",
- "address1": "B-11, Betahouse",
- "address2": "Street 11, Sector 52",
- "city": "Manhattan",
- "province": "New York",
- "country": "United States",
- "zip": "10027",
- "phone": "145-112211",
- "name": "Andrew Wyatt",
- "province_code": "NY",
- "country_code": "US",
- "country_name": "United States",
- "default": true
- }
- ]
- }
-}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_item.json b/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_item.json
deleted file mode 100644
index 296dede..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_item.json
+++ /dev/null
@@ -1,125 +0,0 @@
-{
- "product": {
- "id": 4059739520,
- "title": "Shopify Test Item",
- "body_html": "<div>Hold back Spin Medallion-Set of 2</div>\n<div></div>\n<div>Finish: Plated/ Powder Coated</div>\n<div>Material: Iron</div>\n<div>Color Finish: Satin Silver, Brown Oil Rubbed, Roman Bronze</div>\n<div>Qty: 1 Set</div>",
- "vendor": "Boa casa",
- "product_type": "Curtain Accessories",
- "created_at": "2016-01-18T17:16:37+05:30",
- "handle": "1001624-01",
- "updated_at": "2016-01-20T17:26:44+05:30",
- "published_at": "2016-01-18T17:16:37+05:30",
- "template_suffix": null,
- "published_scope": "global",
- "tags": "Category_Curtain Accessories, Type_Holdback",
- "variants": [{
- "id": 13917612359,
- "product_id": 4059739520,
- "title": "Test BALCK Item",
- "price": "499.00",
- "sku": "",
- "position": 1,
- "grams": 0,
- "inventory_policy": "continue",
- "compare_at_price": null,
- "fulfillment_service": "manual",
- "inventory_management": "shopify",
- "option1": "BLACK",
- "option2": null,
- "option3": null,
- "created_at": "2016-01-18T17:16:37+05:30",
- "updated_at": "2016-01-20T17:26:44+05:30",
- "requires_shipping": true,
- "taxable": true,
- "barcode": "",
- "inventory_quantity": -1,
- "old_inventory_quantity": -1,
- "image_id": 8539321735,
- "weight": 0,
- "weight_unit": "kg"
- }, {
- "id": 13917612423,
- "product_id": 4059739520,
- "title": "Test BLUE Item",
- "price": "499.00",
- "sku": "",
- "position": 2,
- "grams": 0,
- "inventory_policy": "continue",
- "compare_at_price": null,
- "fulfillment_service": "manual",
- "inventory_management": "shopify",
- "option1": "BLUE",
- "option2": null,
- "option3": null,
- "created_at": "2016-01-18T17:16:37+05:30",
- "updated_at": "2016-01-20T17:26:44+05:30",
- "requires_shipping": true,
- "taxable": true,
- "barcode": "",
- "inventory_quantity": -1,
- "old_inventory_quantity": -1,
- "image_id": null,
- "weight": 0,
- "weight_unit": "kg"
- }, {
- "id": 13917612487,
- "product_id": 4059739520,
- "title": "Test White Item",
- "price": "499.00",
- "sku": "",
- "position": 3,
- "grams": 0,
- "inventory_policy": "continue",
- "compare_at_price": null,
- "fulfillment_service": "manual",
- "inventory_management": "shopify",
- "option1": "White",
- "option2": null,
- "option3": null,
- "created_at": "2016-01-18T17:16:37+05:30",
- "updated_at": "2016-01-18T17:16:37+05:30",
- "requires_shipping": true,
- "taxable": true,
- "barcode": "",
- "inventory_quantity": 0,
- "old_inventory_quantity": 0,
- "image_id": null,
- "weight": 0,
- "weight_unit": "kg"
- }],
- "options": [{
- "id": 4985027399,
- "product_id": 4059739520,
- "name": "Colour",
- "position": 1,
- "values": [
- "BLACK",
- "BLUE",
- "White"
- ]
- }],
- "images": [{
- "id": 8539321735,
- "product_id": 4059739520,
- "position": 1,
- "created_at": "2016-01-18T17:16:37+05:30",
- "updated_at": "2016-01-18T17:16:37+05:30",
- "src": "https://cdn.shopify.com/s/files/1/1123/0654/products/2015-12-17_6.png?v=1453117597",
- "variant_ids": [
- 13917612359
- ]
- }],
- "image": {
- "id": 8539321735,
- "product_id": 4059739520,
- "position": 1,
- "created_at": "2016-01-18T17:16:37+05:30",
- "updated_at": "2016-01-18T17:16:37+05:30",
- "src": "https://cdn.shopify.com/s/files/1/1123/0654/products/2015-12-17_6.png?v=1453117597",
- "variant_ids": [
- 13917612359
- ]
- }
- }
-}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_order.json b/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_order.json
deleted file mode 100644
index 988a2f0..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_order.json
+++ /dev/null
@@ -1,270 +0,0 @@
-{
- "order": {
- "id": 2414345735,
- "email": "andrew@wyatt.co.in",
- "closed_at": null,
- "created_at": "2016-01-20T17:26:39+05:30",
- "updated_at": "2016-01-20T17:27:15+05:30",
- "number": 5,
- "note": "",
- "token": "660fed25987517b733644a8c9ec7c8e0",
- "gateway": "manual",
- "test": false,
- "total_price": "1018.00",
- "subtotal_price": "998.00",
- "total_weight": 0,
- "total_tax": "0.00",
- "taxes_included": false,
- "currency": "INR",
- "financial_status": "paid",
- "confirmed": true,
- "total_discounts": "0.00",
- "total_line_items_price": "998.00",
- "cart_token": null,
- "buyer_accepts_marketing": false,
- "name": "#1005",
- "referring_site": null,
- "landing_site": null,
- "cancelled_at": null,
- "cancel_reason": null,
- "total_price_usd": "15.02",
- "checkout_token": null,
- "reference": null,
- "user_id": 55391175,
- "location_id": null,
- "source_identifier": null,
- "source_url": null,
- "processed_at": "2016-01-20T17:26:39+05:30",
- "device_id": null,
- "browser_ip": null,
- "landing_site_ref": null,
- "order_number": 1005,
- "discount_codes": [],
- "note_attributes": [],
- "payment_gateway_names": [
- "manual"
- ],
- "processing_method": "manual",
- "checkout_id": null,
- "source_name": "shopify_draft_order",
- "fulfillment_status": "fulfilled",
- "tax_lines": [],
- "tags": "",
- "contact_email": "andrew@wyatt.co.in",
- "line_items": [
- {
- "id": 4125768135,
- "variant_id": 13917612359,
- "title": "Shopify Test Item",
- "quantity": 1,
- "price": "499.00",
- "grams": 0,
- "sku": "",
- "variant_title": "Roman BALCK 1",
- "vendor": "Boa casa",
- "fulfillment_service": "manual",
- "product_id": 4059739527,
- "requires_shipping": true,
- "taxable": true,
- "gift_card": false,
- "name": "Roman BALCK 1",
- "variant_inventory_management": "shopify",
- "properties": [],
- "product_exists": true,
- "fulfillable_quantity": 0,
- "total_discount": "0.00",
- "fulfillment_status": "fulfilled",
- "tax_lines": []
- },
- {
- "id": 4125768199,
- "variant_id": 13917612423,
- "title": "Shopify Test Item",
- "quantity": 1,
- "price": "499.00",
- "grams": 0,
- "sku": "",
- "variant_title": "Satin BLUE 1",
- "vendor": "Boa casa",
- "fulfillment_service": "manual",
- "product_id": 4059739527,
- "requires_shipping": true,
- "taxable": true,
- "gift_card": false,
- "name": "Satin BLUE 1",
- "variant_inventory_management": "shopify",
- "properties": [],
- "product_exists": true,
- "fulfillable_quantity": 0,
- "total_discount": "0.00",
- "fulfillment_status": "fulfilled",
- "tax_lines": []
- }
- ],
- "shipping_lines": [
- {
- "id": 2108906247,
- "title": "International Shipping",
- "price": "20.00",
- "code": "International Shipping",
- "source": "shopify",
- "phone": null,
- "tax_lines": []
- }
- ],
- "billing_address": {
- "first_name": "Andrew",
- "address1": "B-11, Betahouse",
- "phone": "145-112211",
- "city": "Manhattan",
- "zip": "10027",
- "province": "New York",
- "country": "United States",
- "last_name": "Wyatt",
- "address2": "Street 11, Sector 52",
- "company": "Wyatt Inc.",
- "latitude": 40.8138912,
- "longitude": -73.96243270000001,
- "name": "Andrew Wyatt",
- "country_code": "US",
- "province_code": "NY"
- },
- "shipping_address": {
- "first_name": "Andrew",
- "address1": "B-11, Betahouse",
- "phone": "145-112211",
- "city": "Manhattan",
- "zip": "10027",
- "province": "New York",
- "country": "United States",
- "last_name": "Wyatt",
- "address2": "Street 11, Sector 52",
- "company": "Wyatt Inc.",
- "latitude": 40.8138912,
- "longitude": -73.96243270000001,
- "name": "Andrew Wyatt",
- "country_code": "US",
- "province_code": "NY"
- },
- "fulfillments": [
- {
- "id": 1849629255,
- "order_id": 2414345735,
- "status": "success",
- "created_at": "2016-01-20T17:27:15+05:30",
- "service": "manual",
- "updated_at": "2016-01-20T17:27:15+05:30",
- "tracking_company": null,
- "tracking_number": null,
- "tracking_numbers": [],
- "tracking_url": null,
- "tracking_urls": [],
- "receipt": {},
- "line_items": [
- {
- "id": 4125768199,
- "variant_id": 13917612423,
- "title": "1001624/01",
- "quantity": 1,
- "price": "499.00",
- "grams": 0,
- "sku": "",
- "variant_title": "Satin Silver",
- "vendor": "Boa casa",
- "fulfillment_service": "manual",
- "product_id": 4059739527,
- "requires_shipping": true,
- "taxable": true,
- "gift_card": false,
- "name": "1001624/01 - Satin Silver",
- "variant_inventory_management": "shopify",
- "properties": [],
- "product_exists": true,
- "fulfillable_quantity": 0,
- "total_discount": "0.00",
- "fulfillment_status": "fulfilled",
- "tax_lines": []
- }
- ]
- },
- {
- "id": 1849628167,
- "order_id": 2414345735,
- "status": "success",
- "created_at": "2016-01-20T17:26:58+05:30",
- "service": "manual",
- "updated_at": "2016-01-20T17:26:58+05:30",
- "tracking_company": null,
- "tracking_number": null,
- "tracking_numbers": [],
- "tracking_url": null,
- "tracking_urls": [],
- "receipt": {},
- "line_items": [
- {
- "id": 4125768135,
- "variant_id": 13917612359,
- "title": "1001624/01",
- "quantity": 1,
- "price": "499.00",
- "grams": 0,
- "sku": "",
- "variant_title": "Roman Bronze",
- "vendor": "Boa casa",
- "fulfillment_service": "manual",
- "product_id": 4059739527,
- "requires_shipping": true,
- "taxable": true,
- "gift_card": false,
- "name": "1001624/01 - Roman Bronze",
- "variant_inventory_management": "shopify",
- "properties": [],
- "product_exists": true,
- "fulfillable_quantity": 0,
- "total_discount": "0.00",
- "fulfillment_status": "fulfilled",
- "tax_lines": []
- }
- ]
- }
- ],
- "refunds": [],
- "customer": {
- "id": 2324518599,
- "email": "andrew@wyatt.co.in",
- "accepts_marketing": false,
- "created_at": "2016-01-20T17:18:35+05:30",
- "updated_at": "2016-01-20T17:26:39+05:30",
- "first_name": "Andrew",
- "last_name": "Wyatt",
- "orders_count": 1,
- "state": "disabled",
- "total_spent": "1018.00",
- "last_order_id": 2414345735,
- "note": "",
- "verified_email": true,
- "multipass_identifier": null,
- "tax_exempt": false,
- "tags": "",
- "last_order_name": "#1005",
- "default_address": {
- "id": 2476804295,
- "first_name": "Andrew",
- "last_name": "Wyatt",
- "company": "Wyatt Inc.",
- "address1": "B-11, Betahouse",
- "address2": "Street 11, Sector 52",
- "city": "Manhattan",
- "province": "New York",
- "country": "United States",
- "zip": "10027",
- "phone": "145-112211",
- "name": "Andrew Wyatt",
- "province_code": "NY",
- "country_code": "US",
- "country_name": "United States",
- "default": true
- }
- }
- }
-}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.js b/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.js
deleted file mode 100644
index b2f82d5..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Shopify Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Shopify Settings
- () => frappe.tests.make('Shopify Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py
deleted file mode 100644
index 6bec301..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import frappe
-
-import unittest, os, json
-from frappe.utils import cstr, cint
-from erpnext.erpnext_integrations.connectors.shopify_connection import create_order
-from erpnext.erpnext_integrations.doctype.shopify_settings.sync_product import make_item
-from erpnext.erpnext_integrations.doctype.shopify_settings.sync_customer import create_customer
-from frappe.core.doctype.data_import.data_import import import_doc
-
-
-class ShopifySettings(unittest.TestCase):
- @classmethod
- def setUpClass(cls):
- frappe.set_user("Administrator")
-
- cls.allow_negative_stock = cint(frappe.db.get_value('Stock Settings', None, 'allow_negative_stock'))
- if not cls.allow_negative_stock:
- frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 1)
-
- # use the fixture data
- import_doc(path=frappe.get_app_path("erpnext", "erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json"))
-
- frappe.reload_doctype("Customer")
- frappe.reload_doctype("Sales Order")
- frappe.reload_doctype("Delivery Note")
- frappe.reload_doctype("Sales Invoice")
-
- cls.setup_shopify()
-
- @classmethod
- def tearDownClass(cls):
- if not cls.allow_negative_stock:
- frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0)
-
- @classmethod
- def setup_shopify(cls):
- shopify_settings = frappe.get_doc("Shopify Settings")
- shopify_settings.taxes = []
-
- shopify_settings.update({
- "app_type": "Private",
- "shopify_url": "test.myshopify.com",
- "api_key": "17702c7c4452b9c5d235240b6e7a39da",
- "password": "17702c7c4452b9c5d235240b6e7a39da",
- "shared_secret": "17702c7c4452b9c5d235240b6e7a39da",
- "price_list": "_Test Price List",
- "warehouse": "_Test Warehouse - _TC",
- "cash_bank_account": "Cash - _TC",
- "account": "Cash - _TC",
- "customer_group": "_Test Customer Group",
- "cost_center": "Main - _TC",
- "taxes": [
- {
- "shopify_tax": "International Shipping",
- "tax_account":"Legal Expenses - _TC"
- }
- ],
- "enable_shopify": 0,
- "sales_order_series": "SO-",
- "sync_sales_invoice": 1,
- "sales_invoice_series": "SINV-",
- "sync_delivery_note": 1,
- "delivery_note_series": "DN-"
- }).save(ignore_permissions=True)
-
- cls.shopify_settings = shopify_settings
-
- def test_order(self):
- # Create Customer
- with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_customer.json")) as shopify_customer:
- shopify_customer = json.load(shopify_customer)
- create_customer(shopify_customer.get("customer"), self.shopify_settings)
-
- # Create Item
- with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_item.json")) as shopify_item:
- shopify_item = json.load(shopify_item)
- make_item("_Test Warehouse - _TC", shopify_item.get("product"))
-
- # Create Order
- with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_order.json")) as shopify_order:
- shopify_order = json.load(shopify_order)
-
- create_order(shopify_order.get("order"), self.shopify_settings, False, company="_Test Company")
-
- sales_order = frappe.get_doc("Sales Order", {"shopify_order_id": cstr(shopify_order.get("order").get("id"))})
-
- self.assertEqual(cstr(shopify_order.get("order").get("id")), sales_order.shopify_order_id)
-
- # Check for customer
- shopify_order_customer_id = cstr(shopify_order.get("order").get("customer").get("id"))
- sales_order_customer_id = frappe.get_value("Customer", sales_order.customer, "shopify_customer_id")
-
- self.assertEqual(shopify_order_customer_id, sales_order_customer_id)
-
- # Check sales invoice
- sales_invoice = frappe.get_doc("Sales Invoice", {"shopify_order_id": sales_order.shopify_order_id})
- self.assertEqual(sales_invoice.rounded_total, sales_order.rounded_total)
-
- # Check delivery note
- delivery_note_count = frappe.db.sql("""select count(*) from `tabDelivery Note`
- where shopify_order_id = %s""", sales_order.shopify_order_id)[0][0]
-
- self.assertEqual(delivery_note_count, len(shopify_order.get("order").get("fulfillments")))
diff --git a/erpnext/erpnext_integrations/doctype/shopify_tax_account/shopify_tax_account.json b/erpnext/erpnext_integrations/doctype/shopify_tax_account/shopify_tax_account.json
deleted file mode 100644
index 63c674c..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_tax_account/shopify_tax_account.json
+++ /dev/null
@@ -1,133 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2015-10-05 16:55:20.455371",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 0,
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "shopify_tax",
- "fieldtype": "Data",
- "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": "Shopify Tax/Shipping 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": 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_2",
- "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": "tax_account",
- "fieldtype": "Link",
- "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": "ERPNext Account",
- "length": 0,
- "no_copy": 0,
- "options": "Account",
- "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,
- "translatable": 0,
- "unique": 0
- }
- ],
- "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,
- "max_attachments": 0,
- "modified": "2018-04-09 11:36:49.272815",
- "modified_by": "Administrator",
- "module": "ERPNext Integrations",
- "name": "Shopify Tax Account",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 0,
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/shopify_tax_account/shopify_tax_account.py b/erpnext/erpnext_integrations/doctype/shopify_tax_account/shopify_tax_account.py
deleted file mode 100644
index 74c13c0..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_tax_account/shopify_tax_account.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, 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 ShopifyTaxAccount(Document):
- pass
diff --git a/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/__init__.py b/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/__init__.py
+++ /dev/null
diff --git a/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/shopify_webhook_detail.json b/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/shopify_webhook_detail.json
deleted file mode 100644
index e47ecdc..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/shopify_webhook_detail.json
+++ /dev/null
@@ -1,103 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2018-04-10 17:06:22.697427",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 0,
- "engine": "InnoDB",
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "webhook_id",
- "fieldtype": "Data",
- "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": "Webhook ID",
- "length": 0,
- "no_copy": 0,
- "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,
- "fieldname": "method",
- "fieldtype": "Data",
- "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": "Method",
- "length": 0,
- "no_copy": 0,
- "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
- }
- ],
- "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,
- "max_attachments": 0,
- "modified": "2018-04-11 12:43:09.456449",
- "modified_by": "Administrator",
- "module": "ERPNext Integrations",
- "name": "Shopify Webhook Detail",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/shopify_webhook_detail.py b/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/shopify_webhook_detail.py
deleted file mode 100644
index e127989..0000000
--- a/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/shopify_webhook_detail.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, 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 ShopifyWebhookDetail(Document):
- pass
diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json
index 4a5e54e..9f9204a 100644
--- a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json
+++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json
@@ -1,22 +1,27 @@
{
- "category": "Modules",
+ "category": "",
"charts": [],
+ "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Marketplace\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Payments\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}]",
"creation": "2020-08-20 19:30:48.138801",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "Integrations",
- "extends_another_page": 1,
- "hide_custom": 1,
+ "extends": "",
+ "extends_another_page": 0,
+ "for_user": "",
+ "hide_custom": 0,
+ "icon": "integration",
"idx": 0,
- "is_standard": 1,
+ "is_default": 0,
+ "is_standard": 0,
"label": "ERPNext Integrations",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Marketplace",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -25,6 +30,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Woocommerce Settings",
+ "link_count": 0,
"link_to": "Woocommerce Settings",
"link_type": "DocType",
"onboard": 0,
@@ -35,6 +41,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Amazon MWS Settings",
+ "link_count": 0,
"link_to": "Amazon MWS Settings",
"link_type": "DocType",
"onboard": 0,
@@ -45,6 +52,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Shopify Settings",
+ "link_count": 0,
"link_to": "Shopify Settings",
"link_type": "DocType",
"onboard": 0,
@@ -54,6 +62,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Payments",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -62,6 +71,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "GoCardless Settings",
+ "link_count": 0,
"link_to": "GoCardless Settings",
"link_type": "DocType",
"onboard": 0,
@@ -72,6 +82,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "M-Pesa Settings",
+ "link_count": 0,
"link_to": "Mpesa Settings",
"link_type": "DocType",
"onboard": 0,
@@ -81,6 +92,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Settings",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -89,6 +101,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Plaid Settings",
+ "link_count": 0,
"link_to": "Plaid Settings",
"link_type": "DocType",
"onboard": 0,
@@ -99,18 +112,26 @@
"hidden": 0,
"is_query_report": 0,
"label": "Exotel Settings",
+ "link_count": 0,
"link_to": "Exotel Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2020-12-01 13:38:35.846528",
+ "modified": "2021-08-05 12:15:58.740246",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "ERPNext Integrations",
+ "onboarding": "",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
- "shortcuts": []
-}
\ No newline at end of file
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 10,
+ "shortcuts": [],
+ "title": "ERPNext Integrations"
+}
diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json
index d258d57..fd4afb8 100644
--- a/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json
+++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json
@@ -1,22 +1,27 @@
{
- "category": "Modules",
+ "category": "",
"charts": [],
+ "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Integrations Settings\", \"col\": 4}}]",
"creation": "2020-07-31 10:38:54.021237",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "Settings",
- "extends_another_page": 1,
+ "extends": "",
+ "extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
+ "icon": "setting",
"idx": 0,
- "is_standard": 1,
+ "is_default": 0,
+ "is_standard": 0,
"label": "ERPNext Integrations Settings",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Integrations Settings",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -25,6 +30,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Woocommerce Settings",
+ "link_count": 0,
"link_to": "Woocommerce Settings",
"link_type": "DocType",
"onboard": 0,
@@ -35,6 +41,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Shopify Settings",
+ "link_count": 0,
"link_to": "Shopify Settings",
"link_type": "DocType",
"onboard": 0,
@@ -45,6 +52,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Amazon MWS Settings",
+ "link_count": 0,
"link_to": "Amazon MWS Settings",
"link_type": "DocType",
"onboard": 0,
@@ -55,6 +63,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Plaid Settings",
+ "link_count": 0,
"link_to": "Plaid Settings",
"link_type": "DocType",
"onboard": 0,
@@ -65,18 +74,26 @@
"hidden": 0,
"is_query_report": 0,
"label": "Exotel Settings",
+ "link_count": 0,
"link_to": "Exotel Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2020-12-01 13:38:34.732552",
+ "modified": "2021-08-05 12:15:58.951704",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "ERPNext Integrations Settings",
+ "onboarding": "",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
- "shortcuts": []
-}
\ No newline at end of file
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 11,
+ "shortcuts": [],
+ "title": "ERPNext Integrations Settings"
+}
diff --git a/erpnext/healthcare/workspace/healthcare/healthcare.json b/erpnext/healthcare/workspace/healthcare/healthcare.json
index b93dda2..55132f3 100644
--- a/erpnext/healthcare/workspace/healthcare/healthcare.json
+++ b/erpnext/healthcare/workspace/healthcare/healthcare.json
@@ -1,5 +1,5 @@
{
- "category": "Domains",
+ "category": "",
"charts": [
{
"chart_name": "Patient Appointments",
@@ -7,22 +7,27 @@
}
],
"charts_label": "",
+ "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Healthcare\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Patient Appointments\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Patient Appointment\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Patient\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Healthcare Service Unit\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Healthcare Practitioner\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Patient History\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Consultation Setup\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Consultation\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Laboratory Setup\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Laboratory\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Rehabilitation and Physiotherapy\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Records and History\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
"creation": "2020-03-02 17:23:17.919682",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "healthcare",
"idx": 0,
- "is_standard": 1,
+ "is_default": 0,
+ "is_standard": 0,
"label": "Healthcare",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Masters",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -31,6 +36,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Patient",
+ "link_count": 0,
"link_to": "Patient",
"link_type": "DocType",
"onboard": 1,
@@ -41,6 +47,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Healthcare Practitioner",
+ "link_count": 0,
"link_to": "Healthcare Practitioner",
"link_type": "DocType",
"onboard": 1,
@@ -51,6 +58,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Practitioner Schedule",
+ "link_count": 0,
"link_to": "Practitioner Schedule",
"link_type": "DocType",
"onboard": 1,
@@ -61,6 +69,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Medical Department",
+ "link_count": 0,
"link_to": "Medical Department",
"link_type": "DocType",
"onboard": 0,
@@ -71,6 +80,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Healthcare Service Unit Type",
+ "link_count": 0,
"link_to": "Healthcare Service Unit Type",
"link_type": "DocType",
"onboard": 0,
@@ -81,6 +91,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Healthcare Service Unit",
+ "link_count": 0,
"link_to": "Healthcare Service Unit",
"link_type": "DocType",
"onboard": 0,
@@ -91,6 +102,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Medical Code Standard",
+ "link_count": 0,
"link_to": "Medical Code Standard",
"link_type": "DocType",
"onboard": 0,
@@ -101,6 +113,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Medical Code",
+ "link_count": 0,
"link_to": "Medical Code",
"link_type": "DocType",
"onboard": 0,
@@ -110,6 +123,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Consultation Setup",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -118,6 +132,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Appointment Type",
+ "link_count": 0,
"link_to": "Appointment Type",
"link_type": "DocType",
"onboard": 0,
@@ -128,6 +143,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Clinical Procedure Template",
+ "link_count": 0,
"link_to": "Clinical Procedure Template",
"link_type": "DocType",
"onboard": 0,
@@ -138,6 +154,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Prescription Dosage",
+ "link_count": 0,
"link_to": "Prescription Dosage",
"link_type": "DocType",
"onboard": 0,
@@ -148,6 +165,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Prescription Duration",
+ "link_count": 0,
"link_to": "Prescription Duration",
"link_type": "DocType",
"onboard": 0,
@@ -158,6 +176,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Antibiotic",
+ "link_count": 0,
"link_to": "Antibiotic",
"link_type": "DocType",
"onboard": 0,
@@ -167,6 +186,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Consultation",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -175,6 +195,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Patient Appointment",
+ "link_count": 0,
"link_to": "Patient Appointment",
"link_type": "DocType",
"onboard": 0,
@@ -185,6 +206,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Clinical Procedure",
+ "link_count": 0,
"link_to": "Clinical Procedure",
"link_type": "DocType",
"onboard": 0,
@@ -195,6 +217,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Patient Encounter",
+ "link_count": 0,
"link_to": "Patient Encounter",
"link_type": "DocType",
"onboard": 0,
@@ -205,6 +228,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Vital Signs",
+ "link_count": 0,
"link_to": "Vital Signs",
"link_type": "DocType",
"onboard": 0,
@@ -215,6 +239,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Complaint",
+ "link_count": 0,
"link_to": "Complaint",
"link_type": "DocType",
"onboard": 0,
@@ -225,6 +250,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Diagnosis",
+ "link_count": 0,
"link_to": "Diagnosis",
"link_type": "DocType",
"onboard": 0,
@@ -235,6 +261,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Fee Validity",
+ "link_count": 0,
"link_to": "Fee Validity",
"link_type": "DocType",
"onboard": 0,
@@ -244,6 +271,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Settings",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -252,6 +280,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Healthcare Settings",
+ "link_count": 0,
"link_to": "Healthcare Settings",
"link_type": "DocType",
"onboard": 1,
@@ -261,6 +290,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Laboratory Setup",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -269,6 +299,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Lab Test Template",
+ "link_count": 0,
"link_to": "Lab Test Template",
"link_type": "DocType",
"onboard": 0,
@@ -279,6 +310,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Lab Test Sample",
+ "link_count": 0,
"link_to": "Lab Test Sample",
"link_type": "DocType",
"onboard": 0,
@@ -289,6 +321,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Lab Test UOM",
+ "link_count": 0,
"link_to": "Lab Test UOM",
"link_type": "DocType",
"onboard": 0,
@@ -299,6 +332,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Sensitivity",
+ "link_count": 0,
"link_to": "Sensitivity",
"link_type": "DocType",
"onboard": 0,
@@ -308,6 +342,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Laboratory",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -316,6 +351,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Lab Test",
+ "link_count": 0,
"link_to": "Lab Test",
"link_type": "DocType",
"onboard": 0,
@@ -326,6 +362,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Sample Collection",
+ "link_count": 0,
"link_to": "Sample Collection",
"link_type": "DocType",
"onboard": 0,
@@ -336,6 +373,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Dosage Form",
+ "link_count": 0,
"link_to": "Dosage Form",
"link_type": "DocType",
"onboard": 0,
@@ -345,6 +383,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Rehabilitation and Physiotherapy",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -353,6 +392,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Exercise Type",
+ "link_count": 0,
"link_to": "Exercise Type",
"link_type": "DocType",
"onboard": 1,
@@ -363,6 +403,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Therapy Type",
+ "link_count": 0,
"link_to": "Therapy Type",
"link_type": "DocType",
"onboard": 1,
@@ -373,6 +414,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Therapy Plan",
+ "link_count": 0,
"link_to": "Therapy Plan",
"link_type": "DocType",
"onboard": 0,
@@ -383,6 +425,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Therapy Session",
+ "link_count": 0,
"link_to": "Therapy Session",
"link_type": "DocType",
"onboard": 0,
@@ -393,6 +436,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Patient Assessment Template",
+ "link_count": 0,
"link_to": "Patient Assessment Template",
"link_type": "DocType",
"onboard": 0,
@@ -403,6 +447,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Patient Assessment",
+ "link_count": 0,
"link_to": "Patient Assessment",
"link_type": "DocType",
"onboard": 0,
@@ -412,6 +457,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Records and History",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -420,6 +466,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Patient History",
+ "link_count": 0,
"link_to": "patient_history",
"link_type": "Page",
"onboard": 0,
@@ -430,6 +477,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Patient Progress",
+ "link_count": 0,
"link_to": "patient-progress",
"link_type": "Page",
"onboard": 0,
@@ -440,6 +488,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Patient Medical Record",
+ "link_count": 0,
"link_to": "Patient Medical Record",
"link_type": "DocType",
"onboard": 0,
@@ -450,6 +499,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Inpatient Record",
+ "link_count": 0,
"link_to": "Inpatient Record",
"link_type": "DocType",
"onboard": 0,
@@ -459,6 +509,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -467,6 +518,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Patient Appointment Analytics",
+ "link_count": 0,
"link_to": "Patient Appointment Analytics",
"link_type": "Report",
"onboard": 0,
@@ -477,21 +529,26 @@
"hidden": 0,
"is_query_report": 1,
"label": "Lab Test Report",
+ "link_count": 0,
"link_to": "Lab Test Report",
"link_type": "Report",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2020-12-01 13:38:34.841396",
+ "modified": "2021-08-05 12:15:59.434612",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Healthcare",
"onboarding": "Healthcare",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
"restrict_to_domain": "Healthcare",
+ "roles": [],
+ "sequence_id": 13,
"shortcuts": [
{
"color": "Orange",
@@ -532,5 +589,6 @@
"link_to": "Healthcare",
"type": "Dashboard"
}
- ]
+ ],
+ "title": "Healthcare"
}
\ No newline at end of file
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 9717bb9..8f7c7db 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -25,7 +25,8 @@
"Address": "public/js/address.js",
"Communication": "public/js/communication.js",
"Event": "public/js/event.js",
- "Newsletter": "public/js/newsletter.js"
+ "Newsletter": "public/js/newsletter.js",
+ "Contact": "public/js/contact.js"
}
override_doctype_class = {
@@ -340,7 +341,6 @@
"erpnext.projects.doctype.project.project.hourly_reminder",
"erpnext.projects.doctype.project.project.collect_project_status",
"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
- "erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders",
"erpnext.support.doctype.service_level_agreement.service_level_agreement.set_service_level_agreement_variance"
],
"hourly_long": [
@@ -437,7 +437,8 @@
'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption',
'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period',
'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields',
- 'erpnext.assets.doctype.asset.asset.get_depreciation_amount': 'erpnext.regional.india.utils.get_depreciation_amount'
+ 'erpnext.assets.doctype.asset.asset.get_depreciation_amount': 'erpnext.regional.india.utils.get_depreciation_amount',
+ 'erpnext.stock.doctype.item.item.set_item_tax_from_hsn_code': 'erpnext.regional.india.utils.set_item_tax_from_hsn_code'
},
'United Arab Emirates': {
'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data',
diff --git a/erpnext/hr/doctype/appraisal/appraisal.py b/erpnext/hr/doctype/appraisal/appraisal.py
index f760187..c2ed457 100644
--- a/erpnext/hr/doctype/appraisal/appraisal.py
+++ b/erpnext/hr/doctype/appraisal/appraisal.py
@@ -9,7 +9,7 @@
from frappe import _
from frappe.model.mapper import get_mapped_doc
from frappe.model.document import Document
-from erpnext.hr.utils import set_employee_name
+from erpnext.hr.utils import set_employee_name, validate_active_employee
class Appraisal(Document):
def validate(self):
@@ -19,6 +19,7 @@
if not self.goals:
frappe.throw(_("Goals cannot be empty"))
+ validate_active_employee(self.employee)
set_employee_name(self)
self.validate_dates()
self.validate_existing_appraisal()
diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py
index 3412675..f79f0fe 100644
--- a/erpnext/hr/doctype/attendance/attendance.py
+++ b/erpnext/hr/doctype/attendance/attendance.py
@@ -8,11 +8,13 @@
from frappe import _
from frappe.model.document import Document
from frappe.utils import cstr, get_datetime, formatdate
+from erpnext.hr.utils import validate_active_employee
class Attendance(Document):
def validate(self):
from erpnext.controllers.status_updater import validate_status
validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"])
+ validate_active_employee(self.employee)
self.validate_attendance_date()
self.validate_duplicate_record()
self.validate_employee_status()
diff --git a/erpnext/hr/doctype/attendance_request/attendance_request.py b/erpnext/hr/doctype/attendance_request/attendance_request.py
index 090d532..7f88fed 100644
--- a/erpnext/hr/doctype/attendance_request/attendance_request.py
+++ b/erpnext/hr/doctype/attendance_request/attendance_request.py
@@ -8,10 +8,11 @@
from frappe.model.document import Document
from frappe.utils import date_diff, add_days, getdate
from erpnext.hr.doctype.employee.employee import is_holiday
-from erpnext.hr.utils import validate_dates
+from erpnext.hr.utils import validate_dates, validate_active_employee
class AttendanceRequest(Document):
def validate(self):
+ validate_active_employee(self.employee)
validate_dates(self, self.from_date, self.to_date)
if self.half_day:
if not getdate(self.from_date)<=getdate(self.half_day_date)<=getdate(self.to_date):
diff --git a/erpnext/hr/doctype/attendance_request/test_attendance_request.py b/erpnext/hr/doctype/attendance_request/test_attendance_request.py
index 3c42bd9..9e668aa 100644
--- a/erpnext/hr/doctype/attendance_request/test_attendance_request.py
+++ b/erpnext/hr/doctype/attendance_request/test_attendance_request.py
@@ -15,7 +15,11 @@
for doctype in ["Attendance Request", "Attendance"]:
frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype))
+ def tearDown(self):
+ frappe.db.rollback()
+
def test_on_duty_attendance_request(self):
+ "Test creation/updation of Attendace from Attendance Request, on duty."
today = nowdate()
employee = get_employee()
attendance_request = frappe.new_doc("Attendance Request")
@@ -26,17 +30,36 @@
attendance_request.company = "_Test Company"
attendance_request.insert()
attendance_request.submit()
- attendance = frappe.get_doc('Attendance', {
- 'employee': employee.name,
- 'attendance_date': date(date.today().year, 1, 1),
- 'docstatus': 1
- })
- self.assertEqual(attendance.status, 'Present')
+
+ attendance = frappe.db.get_value(
+ "Attendance",
+ filters={
+ "attendance_request": attendance_request.name,
+ "attendance_date": date(date.today().year, 1, 1)
+ },
+ fieldname=["status", "docstatus"],
+ as_dict=True
+ )
+ self.assertEqual(attendance.status, "Present")
+ self.assertEqual(attendance.docstatus, 1)
+
+ # cancelling attendance request cancels linked attendances
attendance_request.cancel()
- attendance.reload()
- self.assertEqual(attendance.docstatus, 2)
+
+ # cancellation alters docname
+ # fetch attendance value again to avoid stale docname
+ attendance_docstatus = frappe.db.get_value(
+ "Attendance",
+ filters={
+ "attendance_request": attendance_request.name,
+ "attendance_date": date(date.today().year, 1, 1)
+ },
+ fieldname="docstatus"
+ )
+ self.assertEqual(attendance_docstatus, 2)
def test_work_from_home_attendance_request(self):
+ "Test creation/updation of Attendace from Attendance Request, work from home."
today = nowdate()
employee = get_employee()
attendance_request = frappe.new_doc("Attendance Request")
@@ -47,15 +70,30 @@
attendance_request.company = "_Test Company"
attendance_request.insert()
attendance_request.submit()
- attendance = frappe.get_doc('Attendance', {
- 'employee': employee.name,
- 'attendance_date': date(date.today().year, 1, 1),
- 'docstatus': 1
- })
- self.assertEqual(attendance.status, 'Work From Home')
+
+ attendance_status = frappe.db.get_value(
+ "Attendance",
+ filters={
+ "attendance_request": attendance_request.name,
+ "attendance_date": date(date.today().year, 1, 1)
+ },
+ fieldname="status"
+ )
+ self.assertEqual(attendance_status, 'Work From Home')
+
attendance_request.cancel()
- attendance.reload()
- self.assertEqual(attendance.docstatus, 2)
+
+ # cancellation alters docname
+ # fetch attendance value again to avoid stale docname
+ attendance_docstatus = frappe.db.get_value(
+ "Attendance",
+ filters={
+ "attendance_request": attendance_request.name,
+ "attendance_date": date(date.today().year, 1, 1)
+ },
+ fieldname="docstatus"
+ )
+ self.assertEqual(attendance_docstatus, 2)
def get_employee():
return frappe.get_doc("Employee", "_T-Employee-00001")
diff --git a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
index a6fe429..0d7fded 100644
--- a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
+++ b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
@@ -7,12 +7,13 @@
from frappe import _
from frappe.utils import date_diff, add_days, getdate, cint, format_date
from frappe.model.document import Document
-from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, \
+from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, validate_active_employee, \
get_holidays_for_employee, create_additional_leave_ledger_entry
class CompensatoryLeaveRequest(Document):
def validate(self):
+ validate_active_employee(self.employee)
validate_dates(self, self.work_from_date, self.work_end_date)
if self.half_day:
if not self.half_day_date:
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index fa017d9..5ca4756 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -13,8 +13,10 @@
from erpnext.utilities.transaction_base import delete_events
from frappe.utils.nestedset import NestedSet
-class EmployeeUserDisabledError(frappe.ValidationError): pass
-class EmployeeLeftValidationError(frappe.ValidationError): pass
+class EmployeeUserDisabledError(frappe.ValidationError):
+ pass
+class InactiveEmployeeStatusError(frappe.ValidationError):
+ pass
class Employee(NestedSet):
nsm_parent_field = 'reports_to'
@@ -196,7 +198,7 @@
message += "<br><br><ul><li>" + "</li><li>".join(link_to_employees)
message += "</li></ul><br>"
message += _("Please make sure the employees above report to another Active employee.")
- throw(message, EmployeeLeftValidationError, _("Cannot Relieve Employee"))
+ throw(message, InactiveEmployeeStatusError, _("Cannot Relieve Employee"))
if not self.relieving_date:
throw(_("Please enter relieving date."))
diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py
index 7d652a7..8fc7cf1 100644
--- a/erpnext/hr/doctype/employee/test_employee.py
+++ b/erpnext/hr/doctype/employee/test_employee.py
@@ -7,7 +7,7 @@
import erpnext
import unittest
import frappe.utils
-from erpnext.hr.doctype.employee.employee import EmployeeLeftValidationError
+from erpnext.hr.doctype.employee.employee import InactiveEmployeeStatusError
test_records = frappe.get_test_records('Employee')
@@ -45,10 +45,33 @@
employee2_doc.save()
employee1_doc.reload()
employee1_doc.status = 'Left'
- self.assertRaises(EmployeeLeftValidationError, employee1_doc.save)
+ self.assertRaises(InactiveEmployeeStatusError, employee1_doc.save)
+
+ def test_employee_status_inactive(self):
+ from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+ from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
+ from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
+
+ employee = make_employee("test_employee_status@company.com")
+ employee_doc = frappe.get_doc("Employee", employee)
+ employee_doc.status = "Inactive"
+ employee_doc.save()
+ employee_doc.reload()
+
+ make_holiday_list()
+ frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List")
+
+ frappe.db.sql("""delete from `tabSalary Structure` where name='Test Inactive Employee Salary Slip'""")
+ salary_structure = make_salary_structure("Test Inactive Employee Salary Slip", "Monthly",
+ employee=employee_doc.name, company=employee_doc.company)
+ salary_slip = make_salary_slip(salary_structure.name, employee=employee_doc.name)
+
+ self.assertRaises(InactiveEmployeeStatusError, salary_slip.save)
+
+ def tearDown(self):
+ frappe.db.rollback()
def make_employee(user, company=None, **kwargs):
- ""
if not frappe.db.get_value("User", user):
frappe.get_doc({
"doctype": "User",
@@ -80,4 +103,5 @@
employee.insert()
return employee.name
else:
+ frappe.db.set_value("Employee", {"employee_name":user}, "status", "Active")
return frappe.get_value("Employee", {"employee_name":user}, "name")
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py
index cb72f6b..cbb3cc8 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.py
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.py
@@ -8,6 +8,7 @@
from frappe.model.document import Document
from frappe.utils import flt, nowdate
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
+from erpnext.hr.utils import validate_active_employee
class EmployeeAdvanceOverPayment(frappe.ValidationError):
pass
@@ -18,11 +19,11 @@
'make_payment_via_journal_entry')
def validate(self):
+ validate_active_employee(self.employee)
self.set_status()
def on_cancel(self):
self.ignore_linked_doctypes = ('GL Entry')
- self.set_status()
def set_status(self):
if self.docstatus == 0:
@@ -183,9 +184,9 @@
bank_cash_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment)
if not bank_cash_account:
frappe.throw(_("Please set a Default Cash Account in Company defaults"))
-
+
advance_account_currency = frappe.db.get_value('Account', advance_account, 'account_currency')
-
+
je = frappe.new_doc('Journal Entry')
je.posting_date = nowdate()
je.voucher_type = get_voucher_type(mode_of_payment)
@@ -229,4 +230,4 @@
if mode_of_payment_type == "Bank":
voucher_type = "Bank Entry"
- return voucher_type
\ No newline at end of file
+ return voucher_type
diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py
index 15fbd4e..60ea0f9 100644
--- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py
+++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py
@@ -9,9 +9,11 @@
from frappe import _
from erpnext.hr.doctype.shift_assignment.shift_assignment import get_actual_start_end_datetime_of_shift
+from erpnext.hr.utils import validate_active_employee
class EmployeeCheckin(Document):
def validate(self):
+ validate_active_employee(self.employee)
self.validate_duplicate_log()
self.fetch_shift()
@@ -122,7 +124,7 @@
def calculate_working_hours(logs, check_in_out_type, working_hours_calc_type):
"""Given a set of logs in chronological order calculates the total working hours based on the parameters.
Zero is returned for all invalid cases.
-
+
:param logs: The List of 'Employee Checkin'.
:param check_in_out_type: One of: 'Alternating entries as IN and OUT during the same shift', 'Strictly based on Log Type in Employee Checkin'
:param working_hours_calc_type: One of: 'First Check-in and Last Check-out', 'Every Valid Check-in and Check-out'
diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js
index d6047e1..5d1a024 100644
--- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js
+++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js
@@ -50,28 +50,13 @@
}, __('Create'));
frm.page.set_inner_btn_group_as_primary(__('Create'));
}
- if (frm.doc.docstatus === 1 && frm.doc.project) {
- frappe.call({
- method: "erpnext.hr.utils.get_boarding_status",
- args: {
- "project": frm.doc.project
- },
- callback: function(r) {
- if (r.message) {
- frm.set_value('boarding_status', r.message);
- }
- refresh_field("boarding_status");
- }
- });
- }
-
},
employee_onboarding_template: function(frm) {
frm.set_value("activities" ,"");
if (frm.doc.employee_onboarding_template) {
frappe.call({
- method: "erpnext.hr.utils.get_onboarding_details",
+ method: "erpnext.controllers.employee_boarding_controller.get_onboarding_details",
args: {
"parent": frm.doc.employee_onboarding_template,
"parenttype": "Employee Onboarding Template"
diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json
index 783c757..673e228 100644
--- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json
+++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json
@@ -30,18 +30,14 @@
"fieldtype": "Link",
"label": "Job Applicant",
"options": "Job Applicant",
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"fieldname": "job_offer",
"fieldtype": "Link",
"label": "Job Offer",
"options": "Job Offer",
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"fetch_from": "job_applicant.applicant_name",
@@ -49,116 +45,90 @@
"fieldtype": "Data",
"in_list_view": 1,
"label": "Employee Name",
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"fieldname": "employee",
"fieldtype": "Link",
"label": "Employee",
"options": "Employee",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "date_of_joining",
"fieldtype": "Date",
"in_list_view": 1,
- "label": "Date of Joining",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Date of Joining"
},
{
"allow_on_submit": 1,
+ "default": "Pending",
"fieldname": "boarding_status",
"fieldtype": "Select",
"label": "Status",
- "options": "\nPending\nIn Process\nCompleted",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Pending\nIn Process\nCompleted",
+ "read_only": 1
},
{
"allow_on_submit": 1,
"default": "0",
"fieldname": "notify_users_by_email",
"fieldtype": "Check",
- "label": "Notify users by email",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Notify users by email"
},
{
"fieldname": "column_break_7",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "employee_onboarding_template",
"fieldtype": "Link",
"label": "Employee Onboarding Template",
- "options": "Employee Onboarding Template",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Employee Onboarding Template"
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
- "options": "Company",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Company"
},
{
"fieldname": "department",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Department",
- "options": "Department",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Department"
},
{
"fieldname": "designation",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Designation",
- "options": "Designation",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Designation"
},
{
"fieldname": "employee_grade",
"fieldtype": "Link",
"label": "Employee Grade",
- "options": "Employee Grade",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Employee Grade"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "table_for_activity",
- "fieldtype": "Section Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Section Break"
},
{
"allow_on_submit": 1,
"fieldname": "activities",
"fieldtype": "Table",
"label": "Activities",
- "options": "Employee Boarding Activity",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Employee Boarding Activity"
},
{
"fieldname": "amended_from",
@@ -167,14 +137,12 @@
"no_copy": 1,
"options": "Employee Onboarding",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
}
],
"is_submittable": 1,
"links": [],
- "modified": "2020-06-25 15:22:24.923835",
+ "modified": "2021-06-03 18:01:51.097927",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Onboarding",
diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py
index 6cc2bf5..55fe317 100644
--- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py
+++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py
@@ -5,7 +5,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
-from erpnext.hr.utils import EmployeeBoardingController
+from erpnext.controllers.employee_boarding_controller import EmployeeBoardingController
from frappe.model.mapper import get_mapped_doc
class IncompleteTaskError(frappe.ValidationError): pass
@@ -16,9 +16,9 @@
self.validate_duplicate_employee_onboarding()
def validate_duplicate_employee_onboarding(self):
- emp_onboarding = frappe.db.exists("Employee Onboarding",{"job_applicant": self.job_applicant})
+ emp_onboarding = frappe.db.exists("Employee Onboarding", {"job_applicant": self.job_applicant})
if emp_onboarding and emp_onboarding != self.name:
- frappe.throw(_("Employee Onboarding: {0} is already for Job Applicant: {1}").format(frappe.bold(emp_onboarding), frappe.bold(self.job_applicant)))
+ frappe.throw(_("Employee Onboarding: {0} already exists for Job Applicant: {1}").format(frappe.bold(emp_onboarding), frappe.bold(self.job_applicant)))
def validate_employee_creation(self):
if self.docstatus != 1:
@@ -30,7 +30,7 @@
else:
task_status = frappe.db.get_value("Task", activity.task, "status")
if task_status not in ["Completed", "Cancelled"]:
- frappe.throw(_("All the mandatory Task for employee creation hasn't been done yet."), IncompleteTaskError)
+ frappe.throw(_("All the mandatory tasks for employee creation are not completed yet."), IncompleteTaskError)
def on_submit(self):
super(EmployeeOnboarding, self).on_submit()
diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
index 336e13c..5f7756b 100644
--- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
+++ b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
@@ -11,39 +11,26 @@
from erpnext.hr.doctype.job_offer.test_job_offer import create_job_offer
class TestEmployeeOnboarding(unittest.TestCase):
- def test_employee_onboarding_incomplete_task(self):
+ def setUp(self):
if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}):
frappe.delete_doc('Employee Onboarding', {'employee_name': 'Test Researcher'})
- _set_up()
- applicant = get_job_applicant()
- job_offer = create_job_offer(job_applicant=applicant.name)
- job_offer.submit()
+ project = "Employee Onboarding : Test Researcher - test@researcher.com"
+ frappe.db.sql("delete from tabProject where name=%s", project)
+ frappe.db.sql("delete from tabTask where project=%s", project)
- onboarding = frappe.new_doc('Employee Onboarding')
- onboarding.job_applicant = applicant.name
- onboarding.job_offer = job_offer.name
- onboarding.company = '_Test Company'
- onboarding.designation = 'Researcher'
- onboarding.append('activities', {
- 'activity_name': 'Assign ID Card',
- 'role': 'HR User',
- 'required_for_employee_creation': 1
- })
- onboarding.append('activities', {
- 'activity_name': 'Assign a laptop',
- 'role': 'HR User'
- })
- onboarding.status = 'Pending'
- onboarding.insert()
- onboarding.submit()
+ def test_employee_onboarding_incomplete_task(self):
+ onboarding = create_employee_onboarding()
- project_name = frappe.db.get_value("Project", onboarding.project, "project_name")
+ project_name = frappe.db.get_value('Project', onboarding.project, 'project_name')
self.assertEqual(project_name, 'Employee Onboarding : Test Researcher - test@researcher.com')
# don't allow making employee if onboarding is not complete
self.assertRaises(IncompleteTaskError, make_employee, onboarding.name)
+ # boarding status
+ self.assertEqual(onboarding.boarding_status, 'Pending')
+
# complete the task
project = frappe.get_doc('Project', onboarding.project)
for task in frappe.get_all('Task', dict(project=project.name)):
@@ -51,6 +38,10 @@
task.status = 'Completed'
task.save()
+ # boarding status
+ onboarding.reload()
+ self.assertEqual(onboarding.boarding_status, 'Completed')
+
# make employee
onboarding.reload()
employee = make_employee(onboarding.name)
@@ -61,6 +52,13 @@
employee.insert()
self.assertEqual(employee.employee_name, 'Test Researcher')
+ def tearDown(self):
+ for entry in frappe.get_all('Employee Onboarding'):
+ doc = frappe.get_doc('Employee Onboarding', entry.name)
+ doc.cancel()
+ doc.delete()
+
+
def get_job_applicant():
if frappe.db.exists('Job Applicant', 'Test Researcher - test@researcher.com'):
return frappe.get_doc('Job Applicant', 'Test Researcher - test@researcher.com')
@@ -72,10 +70,35 @@
applicant.insert()
return applicant
-def _set_up():
- for doctype in ["Employee Onboarding"]:
- frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype))
+def get_job_offer(applicant_name):
+ job_offer = frappe.db.exists('Job Offer', {'job_applicant': applicant_name})
+ if job_offer:
+ return frappe.get_doc('Job Offer', job_offer)
- project = "Employee Onboarding : Test Researcher - test@researcher.com"
- frappe.db.sql("delete from tabProject where name=%s", project)
- frappe.db.sql("delete from tabTask where project=%s", project)
+ job_offer = create_job_offer(job_applicant=applicant_name)
+ job_offer.submit()
+ return job_offer
+
+def create_employee_onboarding():
+ applicant = get_job_applicant()
+ job_offer = get_job_offer(applicant.name)
+
+ onboarding = frappe.new_doc('Employee Onboarding')
+ onboarding.job_applicant = applicant.name
+ onboarding.job_offer = job_offer.name
+ onboarding.company = '_Test Company'
+ onboarding.designation = 'Researcher'
+ onboarding.append('activities', {
+ 'activity_name': 'Assign ID Card',
+ 'role': 'HR User',
+ 'required_for_employee_creation': 1
+ })
+ onboarding.append('activities', {
+ 'activity_name': 'Assign a laptop',
+ 'role': 'HR User'
+ })
+ onboarding.status = 'Pending'
+ onboarding.insert()
+ onboarding.submit()
+
+ return onboarding
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_promotion/employee_promotion.py b/erpnext/hr/doctype/employee_promotion/employee_promotion.py
index 83fb235..a3a6183 100644
--- a/erpnext/hr/doctype/employee_promotion/employee_promotion.py
+++ b/erpnext/hr/doctype/employee_promotion/employee_promotion.py
@@ -7,12 +7,11 @@
from frappe import _
from frappe.model.document import Document
from frappe.utils import getdate
-from erpnext.hr.utils import update_employee
+from erpnext.hr.utils import update_employee, validate_active_employee
class EmployeePromotion(Document):
def validate(self):
- if frappe.get_value("Employee", self.employee, "status") != "Active":
- frappe.throw(_("Cannot promote Employee with status Left or Inactive"))
+ validate_active_employee(self.employee)
def before_submit(self):
if getdate(self.promotion_date) > getdate():
diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.py b/erpnext/hr/doctype/employee_referral/employee_referral.py
index 45d6872..0493306 100644
--- a/erpnext/hr/doctype/employee_referral/employee_referral.py
+++ b/erpnext/hr/doctype/employee_referral/employee_referral.py
@@ -7,9 +7,11 @@
from frappe import _
from frappe.utils import get_link_to_form
from frappe.model.document import Document
+from erpnext.hr.utils import validate_active_employee
class EmployeeReferral(Document):
def validate(self):
+ validate_active_employee(self.referrer)
self.set_full_name()
self.set_referral_bonus_payment_status()
diff --git a/erpnext/hr/doctype/employee_separation/employee_separation.js b/erpnext/hr/doctype/employee_separation/employee_separation.js
index 9a75c16..d9011b2 100644
--- a/erpnext/hr/doctype/employee_separation/employee_separation.js
+++ b/erpnext/hr/doctype/employee_separation/employee_separation.js
@@ -23,27 +23,13 @@
frappe.set_route('List', 'Task', {project: frm.doc.project});
},__("View"));
}
- if (frm.doc.docstatus === 1 && frm.doc.project) {
- frappe.call({
- method: "erpnext.hr.utils.get_boarding_status",
- args: {
- "project": frm.doc.project
- },
- callback: function(r) {
- if (r.message) {
- frm.set_value('boarding_status', r.message);
- }
- refresh_field("boarding_status");
- }
- });
- }
},
employee_separation_template: function(frm) {
frm.set_value("activities" ,"");
if (frm.doc.employee_separation_template) {
frappe.call({
- method: "erpnext.hr.utils.get_onboarding_details",
+ method: "erpnext.controllers.employee_boarding_controller.get_onboarding_details",
args: {
"parent": frm.doc.employee_separation_template,
"parenttype": "Employee Separation Template"
diff --git a/erpnext/hr/doctype/employee_separation/employee_separation.json b/erpnext/hr/doctype/employee_separation/employee_separation.json
index 7af20988..c10da5c 100644
--- a/erpnext/hr/doctype/employee_separation/employee_separation.json
+++ b/erpnext/hr/doctype/employee_separation/employee_separation.json
@@ -50,11 +50,12 @@
},
{
"allow_on_submit": 1,
+ "default": "Pending",
"fieldname": "boarding_status",
"fieldtype": "Select",
"label": "Status",
- "options": "\nPending\nIn Process\nCompleted",
- "reqd": 1
+ "options": "Pending\nIn Process\nCompleted",
+ "read_only": 1
},
{
"allow_on_submit": 1,
@@ -147,7 +148,7 @@
],
"is_submittable": 1,
"links": [],
- "modified": "2021-04-28 15:58:36.020196",
+ "modified": "2021-06-03 18:02:54.007313",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Separation",
diff --git a/erpnext/hr/doctype/employee_separation/employee_separation.py b/erpnext/hr/doctype/employee_separation/employee_separation.py
index b646681..8afee25 100644
--- a/erpnext/hr/doctype/employee_separation/employee_separation.py
+++ b/erpnext/hr/doctype/employee_separation/employee_separation.py
@@ -3,7 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from erpnext.hr.utils import EmployeeBoardingController
+from erpnext.controllers.employee_boarding_controller import EmployeeBoardingController
class EmployeeSeparation(EmployeeBoardingController):
def validate(self):
diff --git a/erpnext/hr/doctype/employee_separation/test_employee_separation.py b/erpnext/hr/doctype/employee_separation/test_employee_separation.py
index 713fcf5..f787d9c 100644
--- a/erpnext/hr/doctype/employee_separation/test_employee_separation.py
+++ b/erpnext/hr/doctype/employee_separation/test_employee_separation.py
@@ -6,21 +6,43 @@
import frappe
import unittest
-test_dependencies = ["Employee Onboarding"]
+test_dependencies = ['Employee Onboarding']
class TestEmployeeSeparation(unittest.TestCase):
def test_employee_separation(self):
- employee = frappe.db.get_value("Employee", {"status": "Active"})
- separation = frappe.new_doc('Employee Separation')
- separation.employee = employee
- separation.company = '_Test Company'
- separation.append('activities', {
- 'activity_name': 'Deactivate Employee',
- 'role': 'HR User'
- })
- separation.boarding_status = 'Pending'
- separation.insert()
- separation.submit()
+ separation = create_employee_separation()
+
self.assertEqual(separation.docstatus, 1)
+ self.assertEqual(separation.boarding_status, 'Pending')
+
+ project = frappe.get_doc('Project', separation.project)
+ project.percent_complete_method = 'Manual'
+ project.status = 'Completed'
+ project.save()
+
+ separation.reload()
+ self.assertEqual(separation.boarding_status, 'Completed')
+
separation.cancel()
- self.assertEqual(separation.project, "")
\ No newline at end of file
+ self.assertEqual(separation.project, '')
+
+ def tearDown(self):
+ for entry in frappe.get_all('Employee Separation'):
+ doc = frappe.get_doc('Employee Separation', entry.name)
+ if doc.docstatus == 1:
+ doc.cancel()
+ doc.delete()
+
+def create_employee_separation():
+ employee = frappe.db.get_value('Employee', {'status': 'Active'})
+ separation = frappe.new_doc('Employee Separation')
+ separation.employee = employee
+ separation.company = '_Test Company'
+ separation.append('activities', {
+ 'activity_name': 'Deactivate Employee',
+ 'role': 'HR User'
+ })
+ separation.boarding_status = 'Pending'
+ separation.insert()
+ separation.submit()
+ return separation
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.py b/erpnext/hr/doctype/employee_transfer/employee_transfer.py
index 6eec9fa..c200774 100644
--- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py
+++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.py
@@ -10,10 +10,6 @@
from erpnext.hr.utils import update_employee
class EmployeeTransfer(Document):
- def validate(self):
- if frappe.get_value("Employee", self.employee, "status") != "Active":
- frappe.throw(_("Cannot transfer Employee with status Left or Inactive"))
-
def before_submit(self):
if getdate(self.transfer_date) > getdate():
frappe.throw(_("Employee Transfer cannot be submitted before Transfer Date"),
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py
index 5010fc3..95e2806 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -6,7 +6,7 @@
from frappe import _
from frappe.utils import get_fullname, flt, cstr, get_link_to_form
from frappe.model.document import Document
-from erpnext.hr.utils import set_employee_name, share_doc_with_approver
+from erpnext.hr.utils import set_employee_name, share_doc_with_approver, validate_active_employee
from erpnext.accounts.party import get_party_account
from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
@@ -23,6 +23,7 @@
'make_payment_via_journal_entry')
def validate(self):
+ validate_active_employee(self.employee)
self.validate_advances()
self.validate_sanctioned_amount()
self.calculate_total_amount()
@@ -35,8 +36,8 @@
if self.task and not self.project:
self.project = frappe.db.get_value("Task", self.task, "project")
- def set_status(self):
- self.status = {
+ def set_status(self, update=False):
+ status = {
"0": "Draft",
"1": "Submitted",
"2": "Cancelled"
@@ -44,14 +45,18 @@
paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount)
precision = self.precision("grand_total")
- if (self.is_paid or (flt(self.total_sanctioned_amount) > 0
- and flt(self.grand_total, precision) == flt(paid_amount, precision))) \
- and self.docstatus == 1 and self.approval_status == 'Approved':
- self.status = "Paid"
+ if (self.is_paid or (flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1
+ and flt(self.grand_total, precision) == flt(paid_amount, precision))) and self.approval_status == 'Approved':
+ status = "Paid"
elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 and self.approval_status == 'Approved':
- self.status = "Unpaid"
+ status = "Unpaid"
elif self.docstatus == 1 and self.approval_status == 'Rejected':
- self.status = 'Rejected'
+ status = 'Rejected'
+
+ if update:
+ self.db_set("status", status)
+ else:
+ self.status = status
def on_update(self):
share_doc_with_approver(self, self.expense_approver)
@@ -74,7 +79,7 @@
if self.is_paid:
update_reimbursed_amount(self)
- self.set_status()
+ self.set_status(update=True)
self.update_claimed_amount_in_employee_advance()
def on_cancel(self):
@@ -86,7 +91,6 @@
if self.is_paid:
update_reimbursed_amount(self)
- self.set_status()
self.update_claimed_amount_in_employee_advance()
def update_claimed_amount_in_employee_advance(self):
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index cee6f37..93fb19f 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -5,7 +5,7 @@
import frappe
from frappe import _
from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, get_fullname, add_days, nowdate
-from erpnext.hr.utils import set_employee_name, get_leave_period, share_doc_with_approver
+from erpnext.hr.utils import set_employee_name, get_leave_period, share_doc_with_approver, validate_active_employee
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange
@@ -22,6 +22,7 @@
return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type)
def validate(self):
+ validate_active_employee(self.employee)
set_employee_name(self)
self.validate_dates()
self.validate_balance_leaves()
diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
index e041b7f..912bd8a 100644
--- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
@@ -7,7 +7,7 @@
from frappe import _
from frappe.model.document import Document
from frappe.utils import getdate, nowdate, flt
-from erpnext.hr.utils import set_employee_name
+from erpnext.hr.utils import set_employee_name, validate_active_employee
from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry
from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves
@@ -15,6 +15,7 @@
class LeaveEncashment(Document):
def validate(self):
set_employee_name(self)
+ validate_active_employee(self.employee)
self.get_leave_details_for_encashment()
self.validate_salary_structure()
diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
index ab65260..89ae4d5 100644
--- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py
+++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
@@ -9,10 +9,12 @@
from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, now_datetime, nowdate
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
+from erpnext.hr.utils import validate_active_employee
from datetime import timedelta, datetime
class ShiftAssignment(Document):
def validate(self):
+ validate_active_employee(self.employee)
self.validate_overlapping_dates()
if self.end_date and self.end_date <= self.start_date:
diff --git a/erpnext/hr/doctype/shift_request/shift_request.py b/erpnext/hr/doctype/shift_request/shift_request.py
index 177c45e..6461f07 100644
--- a/erpnext/hr/doctype/shift_request/shift_request.py
+++ b/erpnext/hr/doctype/shift_request/shift_request.py
@@ -7,12 +7,13 @@
from frappe import _
from frappe.model.document import Document
from frappe.utils import formatdate, getdate
-from erpnext.hr.utils import share_doc_with_approver
+from erpnext.hr.utils import share_doc_with_approver, validate_active_employee
class OverlapError(frappe.ValidationError): pass
class ShiftRequest(Document):
def validate(self):
+ validate_active_employee(self.employee)
self.validate_dates()
self.validate_shift_request_overlap_dates()
self.validate_approver()
diff --git a/erpnext/hr/doctype/shift_request/test_shift_request.py b/erpnext/hr/doctype/shift_request/test_shift_request.py
index 9c0d8e3..3525540 100644
--- a/erpnext/hr/doctype/shift_request/test_shift_request.py
+++ b/erpnext/hr/doctype/shift_request/test_shift_request.py
@@ -15,24 +15,35 @@
for doctype in ["Shift Request", "Shift Assignment"]:
frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype))
+ def tearDown(self):
+ frappe.db.rollback()
+
def test_make_shift_request(self):
+ "Test creation/updation of Shift Assignment from Shift Request."
department = frappe.get_value("Employee", "_T-Employee-00001", 'department')
set_shift_approver(department)
approver = frappe.db.sql("""select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""", (department))[0][0]
shift_request = make_shift_request(approver)
- shift_assignments = frappe.db.sql('''
- SELECT shift_request, employee
- FROM `tabShift Assignment`
- WHERE shift_request = '{0}'
- '''.format(shift_request.name), as_dict=1)
- for d in shift_assignments:
- employee = d.get('employee')
- self.assertEqual(shift_request.employee, employee)
- shift_request.cancel()
- shift_assignment_doc = frappe.get_doc("Shift Assignment", {"shift_request": d.get('shift_request')})
- self.assertEqual(shift_assignment_doc.docstatus, 2)
+ # Only one shift assignment is created against a shift request
+ shift_assignment = frappe.db.get_value(
+ "Shift Assignment",
+ filters={"shift_request": shift_request.name},
+ fieldname=["employee", "docstatus"],
+ as_dict=True
+ )
+ self.assertEqual(shift_request.employee, shift_assignment.employee)
+ self.assertEqual(shift_assignment.docstatus, 1)
+
+ shift_request.cancel()
+
+ shift_assignment_docstatus = frappe.db.get_value(
+ "Shift Assignment",
+ filters={"shift_request": shift_request.name},
+ fieldname="docstatus"
+ )
+ self.assertEqual(shift_assignment_docstatus, 2)
def test_shift_request_approver_perms(self):
employee = frappe.get_doc("Employee", "_T-Employee-00001")
diff --git a/erpnext/hr/doctype/travel_request/travel_request.py b/erpnext/hr/doctype/travel_request/travel_request.py
index 01d3f34..60834d3 100644
--- a/erpnext/hr/doctype/travel_request/travel_request.py
+++ b/erpnext/hr/doctype/travel_request/travel_request.py
@@ -5,6 +5,8 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
+from erpnext.hr.utils import validate_active_employee
class TravelRequest(Document):
- pass
+ def validate(self):
+ validate_active_employee(self.employee)
diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py
index ebb1734..992b18d 100644
--- a/erpnext/hr/utils.py
+++ b/erpnext/hr/utils.py
@@ -3,128 +3,15 @@
import erpnext
import frappe
-from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
+from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee, InactiveEmployeeStatusError
from frappe import _
from frappe.desk.form import assign_to
from frappe.model.document import Document
from frappe.utils import (add_days, cstr, flt, format_datetime, formatdate,
- get_datetime, getdate, nowdate, today, unique)
-
+ get_datetime, getdate, nowdate, today, unique, get_link_to_form)
class DuplicateDeclarationError(frappe.ValidationError): pass
-
-class EmployeeBoardingController(Document):
- '''
- Create the project and the task for the boarding process
- Assign to the concerned person and roles as per the onboarding/separation template
- '''
- def validate(self):
- # remove the task if linked before submitting the form
- if self.amended_from:
- for activity in self.activities:
- activity.task = ''
-
- def on_submit(self):
- # create the project for the given employee onboarding
- project_name = _(self.doctype) + " : "
- if self.doctype == "Employee Onboarding":
- project_name += self.job_applicant
- else:
- project_name += self.employee
-
- project = frappe.get_doc({
- "doctype": "Project",
- "project_name": project_name,
- "expected_start_date": self.date_of_joining if self.doctype == "Employee Onboarding" else self.resignation_letter_date,
- "department": self.department,
- "company": self.company
- }).insert(ignore_permissions=True, ignore_mandatory=True)
-
- self.db_set("project", project.name)
- self.db_set("boarding_status", "Pending")
- self.reload()
- self.create_task_and_notify_user()
-
- def create_task_and_notify_user(self):
- # create the task for the given project and assign to the concerned person
- for activity in self.activities:
- if activity.task:
- continue
-
- task = frappe.get_doc({
- "doctype": "Task",
- "project": self.project,
- "subject": activity.activity_name + " : " + self.employee_name,
- "description": activity.description,
- "department": self.department,
- "company": self.company,
- "task_weight": activity.task_weight
- }).insert(ignore_permissions=True)
- activity.db_set("task", task.name)
-
- users = [activity.user] if activity.user else []
- if activity.role:
- user_list = frappe.db.sql_list('''
- SELECT
- DISTINCT(has_role.parent)
- FROM
- `tabHas Role` has_role
- LEFT JOIN `tabUser` user
- ON has_role.parent = user.name
- WHERE
- has_role.parenttype = 'User'
- AND user.enabled = 1
- AND has_role.role = %s
- ''', activity.role)
- users = unique(users + user_list)
-
- if "Administrator" in users:
- users.remove("Administrator")
-
- # assign the task the users
- if users:
- self.assign_task_to_users(task, users)
-
- def assign_task_to_users(self, task, users):
- for user in users:
- args = {
- 'assign_to': [user],
- 'doctype': task.doctype,
- 'name': task.name,
- 'description': task.description or task.subject,
- 'notify': self.notify_users_by_email
- }
- assign_to.add(args)
-
- def on_cancel(self):
- # delete task project
- for task in frappe.get_all("Task", filters={"project": self.project}):
- frappe.delete_doc("Task", task.name, force=1)
- frappe.delete_doc("Project", self.project, force=1)
- self.db_set('project', '')
- for activity in self.activities:
- activity.db_set("task", "")
-
-
-@frappe.whitelist()
-def get_onboarding_details(parent, parenttype):
- return frappe.get_all("Employee Boarding Activity",
- fields=["activity_name", "role", "user", "required_for_employee_creation", "description", "task_weight"],
- filters={"parent": parent, "parenttype": parenttype},
- order_by= "idx")
-
-@frappe.whitelist()
-def get_boarding_status(project):
- status = 'Pending'
- if project:
- doc = frappe.get_doc('Project', project)
- if flt(doc.percent_complete) > 0.0 and flt(doc.percent_complete) < 100.0:
- status = 'In Process'
- elif flt(doc.percent_complete) == 100.0:
- status = 'Completed'
- return status
-
def set_employee_name(doc):
if doc.employee and not doc.employee_name:
doc.employee_name = frappe.db.get_value("Employee", doc.employee, "employee_name")
@@ -522,3 +409,8 @@
approver = approvers.get(doc.doctype)
if doc_before_save.get(approver) != doc.get(approver):
frappe.share.remove(doc.doctype, doc.name, doc_before_save.get(approver))
+
+def validate_active_employee(employee):
+ if frappe.db.get_value("Employee", employee, "status") == "Inactive":
+ frappe.throw(_("Transactions cannot be created for an Inactive Employee {0}.").format(
+ get_link_to_form("Employee", employee)), InactiveEmployeeStatusError)
\ No newline at end of file
diff --git a/erpnext/hr/workspace/hr/hr.json b/erpnext/hr/workspace/hr/hr.json
index 4500ba4..575fa7b 100644
--- a/erpnext/hr/workspace/hr/hr.json
+++ b/erpnext/hr/workspace/hr/hr.json
@@ -1,28 +1,32 @@
{
- "category": "Modules",
+ "category": "",
"charts": [
{
"chart_name": "Outgoing Salary",
"label": "Outgoing Salary"
}
],
+ "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Human Resource\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Outgoing Salary\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Employee\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Leave Application\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Attendance\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Job Applicant\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Monthly Attendance Sheet\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Employee\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Employee Lifecycle\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Shift Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Leaves\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Attendance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Expense Claims\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Fleet Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Recruitment\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loans\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Training\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Performance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}]",
"creation": "2020-03-02 15:48:58.322521",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "hr",
"idx": 0,
"is_default": 0,
- "is_standard": 1,
+ "is_standard": 0,
"label": "HR",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Employee",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -31,6 +35,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee",
+ "link_count": 0,
"link_to": "Employee",
"link_type": "DocType",
"onboard": 1,
@@ -41,6 +46,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employment Type",
+ "link_count": 0,
"link_to": "Employment Type",
"link_type": "DocType",
"onboard": 0,
@@ -51,6 +57,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Branch",
+ "link_count": 0,
"link_to": "Branch",
"link_type": "DocType",
"onboard": 0,
@@ -61,6 +68,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Department",
+ "link_count": 0,
"link_to": "Department",
"link_type": "DocType",
"onboard": 0,
@@ -71,6 +79,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Designation",
+ "link_count": 0,
"link_to": "Designation",
"link_type": "DocType",
"onboard": 0,
@@ -81,6 +90,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Grade",
+ "link_count": 0,
"link_to": "Employee Grade",
"link_type": "DocType",
"onboard": 0,
@@ -91,6 +101,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Group",
+ "link_count": 0,
"link_to": "Employee Group",
"link_type": "DocType",
"onboard": 0,
@@ -101,6 +112,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Health Insurance",
+ "link_count": 0,
"link_to": "Employee Health Insurance",
"link_type": "DocType",
"onboard": 0,
@@ -110,6 +122,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Lifecycle",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -118,6 +131,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Onboarding",
+ "link_count": 0,
"link_to": "Employee Onboarding",
"link_type": "DocType",
"onboard": 0,
@@ -128,6 +142,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Skill Map",
+ "link_count": 0,
"link_to": "Employee Skill Map",
"link_type": "DocType",
"onboard": 0,
@@ -138,6 +153,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Promotion",
+ "link_count": 0,
"link_to": "Employee Promotion",
"link_type": "DocType",
"onboard": 0,
@@ -148,6 +164,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Transfer",
+ "link_count": 0,
"link_to": "Employee Transfer",
"link_type": "DocType",
"onboard": 0,
@@ -157,6 +174,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Grievance Type",
+ "link_count": 0,
"link_to": "Grievance Type",
"link_type": "DocType",
"onboard": 0,
@@ -166,6 +184,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Grievance",
+ "link_count": 0,
"link_to": "Employee Grievance",
"link_type": "DocType",
"onboard": 0,
@@ -176,6 +195,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Separation",
+ "link_count": 0,
"link_to": "Employee Separation",
"link_type": "DocType",
"onboard": 0,
@@ -186,6 +206,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Onboarding Template",
+ "link_count": 0,
"link_to": "Employee Onboarding Template",
"link_type": "DocType",
"onboard": 0,
@@ -196,6 +217,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Separation Template",
+ "link_count": 0,
"link_to": "Employee Separation Template",
"link_type": "DocType",
"onboard": 0,
@@ -205,6 +227,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Shift Management",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -213,6 +236,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Shift Type",
+ "link_count": 0,
"link_to": "Shift Type",
"link_type": "DocType",
"onboard": 0,
@@ -223,6 +247,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Shift Request",
+ "link_count": 0,
"link_to": "Shift Request",
"link_type": "DocType",
"onboard": 0,
@@ -233,6 +258,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Shift Assignment",
+ "link_count": 0,
"link_to": "Shift Assignment",
"link_type": "DocType",
"onboard": 0,
@@ -242,6 +268,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Leaves",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -250,6 +277,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Holiday List",
+ "link_count": 0,
"link_to": "Holiday List",
"link_type": "DocType",
"onboard": 0,
@@ -260,6 +288,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Leave Type",
+ "link_count": 0,
"link_to": "Leave Type",
"link_type": "DocType",
"onboard": 0,
@@ -270,6 +299,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Leave Period",
+ "link_count": 0,
"link_to": "Leave Period",
"link_type": "DocType",
"onboard": 0,
@@ -280,6 +310,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Leave Policy",
+ "link_count": 0,
"link_to": "Leave Policy",
"link_type": "DocType",
"onboard": 0,
@@ -290,6 +321,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Leave Policy Assignment",
+ "link_count": 0,
"link_to": "Leave Policy Assignment",
"link_type": "DocType",
"onboard": 0,
@@ -300,6 +332,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Leave Application",
+ "link_count": 0,
"link_to": "Leave Application",
"link_type": "DocType",
"onboard": 0,
@@ -310,6 +343,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Leave Allocation",
+ "link_count": 0,
"link_to": "Leave Allocation",
"link_type": "DocType",
"onboard": 0,
@@ -320,6 +354,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Leave Encashment",
+ "link_count": 0,
"link_to": "Leave Encashment",
"link_type": "DocType",
"onboard": 0,
@@ -330,6 +365,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Leave Block List",
+ "link_count": 0,
"link_to": "Leave Block List",
"link_type": "DocType",
"onboard": 0,
@@ -340,6 +376,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Compensatory Leave Request",
+ "link_count": 0,
"link_to": "Compensatory Leave Request",
"link_type": "DocType",
"onboard": 0,
@@ -349,6 +386,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Attendance",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -357,6 +395,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Attendance Tool",
+ "link_count": 0,
"link_to": "Employee Attendance Tool",
"link_type": "DocType",
"onboard": 1,
@@ -367,6 +406,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Attendance",
+ "link_count": 0,
"link_to": "Attendance",
"link_type": "DocType",
"onboard": 1,
@@ -377,6 +417,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Attendance Request",
+ "link_count": 0,
"link_to": "Attendance Request",
"link_type": "DocType",
"onboard": 0,
@@ -387,6 +428,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Upload Attendance",
+ "link_count": 0,
"link_to": "Upload Attendance",
"link_type": "DocType",
"onboard": 0,
@@ -397,6 +439,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Checkin",
+ "link_count": 0,
"link_to": "Employee Checkin",
"link_type": "DocType",
"onboard": 0,
@@ -406,6 +449,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Expense Claims",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -414,6 +458,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Expense Claim",
+ "link_count": 0,
"link_to": "Expense Claim",
"link_type": "DocType",
"onboard": 0,
@@ -424,6 +469,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Advance",
+ "link_count": 0,
"link_to": "Employee Advance",
"link_type": "DocType",
"onboard": 0,
@@ -433,6 +479,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Travel Request",
+ "link_count": 0,
"link_to": "Travel Request",
"link_type": "DocType",
"onboard": 0,
@@ -442,6 +489,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Settings",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -450,6 +498,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "HR Settings",
+ "link_count": 0,
"link_to": "HR Settings",
"link_type": "DocType",
"onboard": 0,
@@ -460,6 +509,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Daily Work Summary Group",
+ "link_count": 0,
"link_to": "Daily Work Summary Group",
"link_type": "DocType",
"onboard": 0,
@@ -470,6 +520,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Team Updates",
+ "link_count": 0,
"link_to": "team-updates",
"link_type": "Page",
"onboard": 0,
@@ -479,6 +530,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Fleet Management",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -486,6 +538,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Driver",
+ "link_count": 0,
"link_to": "Driver",
"link_type": "DocType",
"onboard": 0,
@@ -496,6 +549,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Vehicle",
+ "link_count": 0,
"link_to": "Vehicle",
"link_type": "DocType",
"onboard": 0,
@@ -506,6 +560,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Vehicle Log",
+ "link_count": 0,
"link_to": "Vehicle Log",
"link_type": "DocType",
"onboard": 0,
@@ -516,6 +571,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Vehicle Expenses",
+ "link_count": 0,
"link_to": "Vehicle Expenses",
"link_type": "Report",
"onboard": 0,
@@ -525,6 +581,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Recruitment",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -533,6 +590,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Job Opening",
+ "link_count": 0,
"link_to": "Job Opening",
"link_type": "DocType",
"onboard": 1,
@@ -542,6 +600,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Referral",
+ "link_count": 0,
"link_to": "Employee Referral",
"link_type": "DocType",
"onboard": 0,
@@ -552,6 +611,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Job Applicant",
+ "link_count": 0,
"link_to": "Job Applicant",
"link_type": "DocType",
"onboard": 1,
@@ -562,6 +622,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Job Offer",
+ "link_count": 0,
"link_to": "Job Offer",
"link_type": "DocType",
"onboard": 1,
@@ -572,6 +633,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Staffing Plan",
+ "link_count": 0,
"link_to": "Staffing Plan",
"link_type": "DocType",
"onboard": 0,
@@ -581,6 +643,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Appointment Letter",
+ "link_count": 0,
"link_to": "Appointment Letter",
"link_type": "DocType",
"onboard": 0,
@@ -590,6 +653,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Appointment Letter Template",
+ "link_count": 0,
"link_to": "Appointment Letter Template",
"link_type": "DocType",
"onboard": 0,
@@ -599,6 +663,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loans",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -607,6 +672,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Application",
+ "link_count": 0,
"link_to": "Loan Application",
"link_type": "DocType",
"onboard": 0,
@@ -617,6 +683,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan",
+ "link_count": 0,
"link_to": "Loan",
"link_type": "DocType",
"onboard": 0,
@@ -627,6 +694,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Type",
+ "link_count": 0,
"link_to": "Loan Type",
"link_type": "DocType",
"onboard": 0,
@@ -636,6 +704,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Training",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -644,6 +713,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Training Program",
+ "link_count": 0,
"link_to": "Training Program",
"link_type": "DocType",
"onboard": 0,
@@ -654,6 +724,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Training Event",
+ "link_count": 0,
"link_to": "Training Event",
"link_type": "DocType",
"onboard": 0,
@@ -664,6 +735,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Training Result",
+ "link_count": 0,
"link_to": "Training Result",
"link_type": "DocType",
"onboard": 0,
@@ -674,6 +746,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Training Feedback",
+ "link_count": 0,
"link_to": "Training Feedback",
"link_type": "DocType",
"onboard": 0,
@@ -683,6 +756,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Performance",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -691,6 +765,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Appraisal",
+ "link_count": 0,
"link_to": "Appraisal",
"link_type": "DocType",
"onboard": 0,
@@ -701,6 +776,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Appraisal Template",
+ "link_count": 0,
"link_to": "Appraisal Template",
"link_type": "DocType",
"onboard": 0,
@@ -711,6 +787,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Energy Point Rule",
+ "link_count": 0,
"link_to": "Energy Point Rule",
"link_type": "DocType",
"onboard": 0,
@@ -721,6 +798,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Energy Point Log",
+ "link_count": 0,
"link_to": "Energy Point Log",
"link_type": "DocType",
"onboard": 0,
@@ -730,6 +808,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Key Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -738,6 +817,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Monthly Attendance Sheet",
+ "link_count": 0,
"link_to": "Monthly Attendance Sheet",
"link_type": "Report",
"onboard": 0,
@@ -748,6 +828,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Recruitment Analytics",
+ "link_count": 0,
"link_to": "Recruitment Analytics",
"link_type": "Report",
"onboard": 0,
@@ -758,6 +839,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Employee Analytics",
+ "link_count": 0,
"link_to": "Employee Analytics",
"link_type": "Report",
"onboard": 0,
@@ -768,6 +850,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Employee Leave Balance",
+ "link_count": 0,
"link_to": "Employee Leave Balance",
"link_type": "Report",
"onboard": 0,
@@ -778,6 +861,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Employee Leave Balance Summary",
+ "link_count": 0,
"link_to": "Employee Leave Balance Summary",
"link_type": "Report",
"onboard": 0,
@@ -788,6 +872,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Employee Advance Summary",
+ "link_count": 0,
"link_to": "Employee Advance Summary",
"link_type": "Report",
"onboard": 0,
@@ -797,6 +882,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Other Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -805,6 +891,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Information",
+ "link_count": 0,
"link_to": "Employee Information",
"link_type": "Report",
"onboard": 0,
@@ -815,6 +902,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Employee Birthday",
+ "link_count": 0,
"link_to": "Employee Birthday",
"link_type": "Report",
"onboard": 0,
@@ -825,6 +913,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Employees Working on a Holiday",
+ "link_count": 0,
"link_to": "Employees working on a holiday",
"link_type": "Report",
"onboard": 0,
@@ -835,20 +924,26 @@
"hidden": 0,
"is_query_report": 1,
"label": "Daily Work Summary Replies",
+ "link_count": 0,
"link_to": "Daily Work Summary Replies",
"link_type": "Report",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2021-05-13 17:19:40.524444",
+ "modified": "2021-08-05 12:15:59.842918",
"modified_by": "Administrator",
"module": "HR",
"name": "HR",
"onboarding": "Human Resource",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 14,
"shortcuts": [
{
"color": "Green",
@@ -889,5 +984,6 @@
"stats_filter": "{\n \"status\": \"Open\"\n}",
"type": "Dashboard"
}
- ]
+ ],
+ "title": "HR"
}
\ No newline at end of file
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.js b/erpnext/loan_management/doctype/loan_application/loan_application.js
index 017026c..5142178 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.js
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.js
@@ -14,7 +14,7 @@
refresh: function(frm) {
frm.trigger("toggle_fields");
frm.trigger("add_toolbar_buttons");
- frm.set_query("loan_type", () => {
+ frm.set_query('loan_type', () => {
return {
filters: {
company: frm.doc.company
diff --git a/erpnext/loan_management/workspace/loan_management/loan_management.json b/erpnext/loan_management/workspace/loan_management/loan_management.json
index d0b67f7..ca528ec 100644
--- a/erpnext/loan_management/workspace/loan_management/loan_management.json
+++ b/erpnext/loan_management/workspace/loan_management/loan_management.json
@@ -1,23 +1,27 @@
{
- "category": "Modules",
+ "category": "",
"charts": [],
+ "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Loan Application\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Loan\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan Processes\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Disbursement and Repayment\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan Security\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
"creation": "2020-03-12 16:35:55.299820",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "loan",
"idx": 0,
"is_default": 0,
- "is_standard": 1,
+ "is_standard": 0,
"label": "Loans",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Loan",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -26,6 +30,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Type",
+ "link_count": 0,
"link_to": "Loan Type",
"link_type": "DocType",
"onboard": 0,
@@ -36,6 +41,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Application",
+ "link_count": 0,
"link_to": "Loan Application",
"link_type": "DocType",
"onboard": 0,
@@ -46,6 +52,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan",
+ "link_count": 0,
"link_to": "Loan",
"link_type": "DocType",
"onboard": 0,
@@ -55,6 +62,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Processes",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -63,6 +71,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Process Loan Security Shortfall",
+ "link_count": 0,
"link_to": "Process Loan Security Shortfall",
"link_type": "DocType",
"onboard": 0,
@@ -73,6 +82,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Process Loan Interest Accrual",
+ "link_count": 0,
"link_to": "Process Loan Interest Accrual",
"link_type": "DocType",
"onboard": 0,
@@ -82,6 +92,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Disbursement and Repayment",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -90,6 +101,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Disbursement",
+ "link_count": 0,
"link_to": "Loan Disbursement",
"link_type": "DocType",
"onboard": 0,
@@ -100,6 +112,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Repayment",
+ "link_count": 0,
"link_to": "Loan Repayment",
"link_type": "DocType",
"onboard": 0,
@@ -110,6 +123,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Write Off",
+ "link_count": 0,
"link_to": "Loan Write Off",
"link_type": "DocType",
"onboard": 0,
@@ -120,6 +134,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Interest Accrual",
+ "link_count": 0,
"link_to": "Loan Interest Accrual",
"link_type": "DocType",
"onboard": 0,
@@ -129,6 +144,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Security",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -137,6 +153,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Security Type",
+ "link_count": 0,
"link_to": "Loan Security Type",
"link_type": "DocType",
"onboard": 0,
@@ -147,6 +164,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Security Price",
+ "link_count": 0,
"link_to": "Loan Security Price",
"link_type": "DocType",
"onboard": 0,
@@ -157,6 +175,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Security",
+ "link_count": 0,
"link_to": "Loan Security",
"link_type": "DocType",
"onboard": 0,
@@ -167,6 +186,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Security Pledge",
+ "link_count": 0,
"link_to": "Loan Security Pledge",
"link_type": "DocType",
"onboard": 0,
@@ -177,6 +197,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Security Unpledge",
+ "link_count": 0,
"link_to": "Loan Security Unpledge",
"link_type": "DocType",
"onboard": 0,
@@ -187,6 +208,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Security Shortfall",
+ "link_count": 0,
"link_to": "Loan Security Shortfall",
"link_type": "DocType",
"onboard": 0,
@@ -196,6 +218,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -204,6 +227,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Loan Repayment and Closure",
+ "link_count": 0,
"link_to": "Loan Repayment and Closure",
"link_type": "Report",
"onboard": 0,
@@ -214,19 +238,26 @@
"hidden": 0,
"is_query_report": 1,
"label": "Loan Security Status",
+ "link_count": 0,
"link_to": "Loan Security Status",
"link_type": "Report",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2021-05-25 17:31:53.586508",
+ "modified": "2021-08-05 12:18:13.350904",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loans",
+ "onboarding": "",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 16,
"shortcuts": [
{
"color": "Green",
@@ -247,5 +278,6 @@
"link_to": "Loan Dashboard",
"type": "Dashboard"
}
- ]
+ ],
+ "title": "Loans"
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js
index c566688..3f50b41 100644
--- a/erpnext/manufacturing/doctype/bom/bom.js
+++ b/erpnext/manufacturing/doctype/bom/bom.js
@@ -83,7 +83,7 @@
if (!frm.doc.__islocal && frm.doc.docstatus<2) {
frm.add_custom_button(__("Update Cost"), function() {
- frm.events.update_cost(frm);
+ frm.events.update_cost(frm, true);
});
frm.add_custom_button(__("Browse BOM"), function() {
frappe.route_options = {
@@ -318,14 +318,15 @@
})
},
- update_cost: function(frm) {
+ update_cost: function(frm, save_doc=false) {
return frappe.call({
doc: frm.doc,
method: "update_cost",
freeze: true,
args: {
update_parent: true,
- from_child_bom:false
+ save: save_doc,
+ from_child_bom: false
},
callback: function(r) {
refresh_field("items");
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 9da461f..0ba8507 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -330,7 +330,7 @@
frappe.get_doc("BOM", bom).update_cost(from_child_bom=True)
if not from_child_bom:
- frappe.msgprint(_("Cost Updated"))
+ frappe.msgprint(_("Cost Updated"), alert=True)
def update_parent_cost(self):
if self.total_cost:
@@ -717,9 +717,8 @@
"ignore_conversion_rate": True
})
item_doc = frappe.get_cached_doc("Item", args.get("item_code"))
- out = frappe._dict()
- get_price_list_rate(bom_args, item_doc, out)
- rate = out.price_list_rate
+ price_list_data = get_price_list_rate(bom_args, item_doc)
+ rate = price_list_data.price_list_rate
return rate
@@ -748,7 +747,7 @@
if valuation_rate <= 0:
last_valuation_rate = frappe.db.sql("""select valuation_rate
from `tabStock Ledger Entry`
- where item_code = %s and valuation_rate > 0
+ where item_code = %s and valuation_rate > 0 and is_cancelled = 0
order by posting_date desc, posting_time desc, creation desc limit 1""", args['item_code'])
valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0
@@ -774,7 +773,7 @@
item.image,
bom.project,
bom_item.rate,
- bom_item.amount,
+ sum(bom_item.{qty_field}/ifnull(bom.quantity, 1)) * bom_item.rate * %(qty)s as amount,
item.stock_uom,
item.item_group,
item.allow_alternative_item,
@@ -1069,13 +1068,6 @@
if barcodes:
or_cond_filters["name"] = ("in", barcodes)
- for cond in get_match_cond(doctype, as_condition=False):
- for key, value in cond.items():
- if key == doctype:
- key = "name"
-
- query_filters[key] = ("in", value)
-
if filters and filters.get("item_code"):
has_variants = frappe.get_cached_value("Item", filters.get("item_code"), "has_variants")
if not has_variants:
@@ -1084,7 +1076,7 @@
if filters and filters.get("is_stock_item"):
query_filters["is_stock_item"] = 1
- return frappe.get_all("Item",
+ return frappe.get_list("Item",
fields = fields, filters=query_filters,
or_filters = or_cond_filters, order_by=order_by,
limit_start=start, limit_page_length=page_len, as_list=1)
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 420bb00..66e2394 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -192,11 +192,11 @@
"completed_qty": args.get("completed_qty") or 0.0
})
elif args.get("start_time"):
- new_args = {
+ new_args = frappe._dict({
"from_time": get_datetime(args.get("start_time")),
"operation": args.get("sub_operation"),
"completed_qty": 0.0
- }
+ })
if employees:
for name in employees:
@@ -608,6 +608,11 @@
target.set_missing_values()
target.set_stock_entry_type()
+ wo_allows_alternate_item = frappe.db.get_value("Work Order", target.work_order, "allow_alternative_item")
+ for item in target.items:
+ item.allow_alternative_item = int(wo_allows_alternate_item and
+ frappe.get_cached_value("Item", item.item_code, "allow_alternative_item"))
+
doclist = get_mapped_doc("Job Card", source_name, {
"Job Card": {
"doctype": "Stock Entry",
@@ -698,4 +703,4 @@
}
}, target_doc, set_missing_values)
- return doclist
\ No newline at end of file
+ return doclist
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 38a0ee7..6a024f2 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -747,9 +747,8 @@
group by item_code, warehouse
""".format(conditions=conditions), { "item_code": row['item_code'] }, as_dict=1)
-def get_warehouse_list(warehouses, warehouse_list=None):
- if not warehouse_list:
- warehouse_list = []
+def get_warehouse_list(warehouses):
+ warehouse_list = []
if isinstance(warehouses, str):
warehouses = json.loads(warehouses)
@@ -761,23 +760,19 @@
else:
warehouse_list.append(row.get("warehouse"))
+ return warehouse_list
+
@frappe.whitelist()
def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_data=None):
if isinstance(doc, str):
doc = frappe._dict(json.loads(doc))
- warehouse_list = []
if warehouses:
- get_warehouse_list(warehouses, warehouse_list)
-
- if warehouse_list:
- warehouses = list(set(warehouse_list))
+ warehouses = list(set(get_warehouse_list(warehouses)))
if doc.get("for_warehouse") and not get_parent_warehouse_data and doc.get("for_warehouse") in warehouses:
warehouses.remove(doc.get("for_warehouse"))
- warehouse_list = None
-
doc['mr_items'] = []
po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index cce1bb6..93e6d7a 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -10,7 +10,7 @@
from erpnext.manufacturing.doctype.production_plan.production_plan import get_sales_orders
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
-from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests
+from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests, get_warehouse_list
class TestProductionPlan(unittest.TestCase):
def setUp(self):
@@ -251,6 +251,27 @@
pln.cancel()
frappe.delete_doc("Production Plan", pln.name)
+ def test_get_warehouse_list_group(self):
+ """Check if required warehouses are returned"""
+ warehouse_json = '[{\"warehouse\":\"_Test Warehouse Group - _TC\"}]'
+
+ warehouses = set(get_warehouse_list(warehouse_json))
+ expected_warehouses = {"_Test Warehouse Group-C1 - _TC", "_Test Warehouse Group-C2 - _TC"}
+
+ missing_warehouse = expected_warehouses - warehouses
+
+ self.assertTrue(len(missing_warehouse) == 0,
+ msg=f"Following warehouses were expected {', '.join(missing_warehouse)}")
+
+ def test_get_warehouse_list_single(self):
+ warehouse_json = '[{\"warehouse\":\"_Test Scrap Warehouse - _TC\"}]'
+
+ warehouses = set(get_warehouse_list(warehouse_json))
+ expected_warehouses = {"_Test Scrap Warehouse - _TC", }
+
+ self.assertEqual(warehouses, expected_warehouses)
+
+
def create_production_plan(**args):
args = frappe._dict(args)
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 0a8e532..282b5d0 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -487,21 +487,20 @@
return
operations = []
- if not self.use_multi_level_bom:
- bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity")
- operations.extend(_get_operations(self.bom_no, qty=1.0/bom_qty))
- else:
+
+ if self.use_multi_level_bom:
bom_tree = frappe.get_doc("BOM", self.bom_no).get_tree_representation()
- bom_traversal = list(reversed(bom_tree.level_order_traversal()))
- bom_traversal.append(bom_tree) # add operation on top level item last
+ bom_traversal = reversed(bom_tree.level_order_traversal())
- for d in bom_traversal:
- if d.is_bom:
- operations.extend(_get_operations(d.name, qty=d.exploded_qty))
+ for node in bom_traversal:
+ if node.is_bom:
+ operations.extend(_get_operations(node.name, qty=node.exploded_qty))
- for correct_index, operation in enumerate(operations, start=1):
- operation.idx = correct_index
+ bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity")
+ operations.extend(_get_operations(self.bom_no, qty=1.0/bom_qty))
+ for correct_index, operation in enumerate(operations, start=1):
+ operation.idx = correct_index
self.set('operations', operations)
self.calculate_time()
@@ -656,7 +655,7 @@
for item in sorted(item_dict.values(), key=lambda d: d['idx'] or 9999):
self.append('required_items', {
'rate': item.rate,
- 'amount': item.amount,
+ 'amount': item.rate * item.qty,
'operation': item.operation or operation,
'item_code': item.item_code,
'item_name': item.item_name,
diff --git a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
index a355203..84eabcd 100644
--- a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
+++ b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
@@ -1,26 +1,31 @@
{
- "category": "Domains",
+ "category": "",
"charts": [
{
"chart_name": "Produced Quantity"
}
],
+ "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Manufacturing\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": null, \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"BOM\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Work Order\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Production Plan\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Forecasting\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Work Order Summary\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"BOM Stock Report\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Production Planning Report\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Production\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Bill of Materials\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tools\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}]",
"creation": "2020-03-02 17:11:37.032604",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "organization",
"idx": 0,
- "is_standard": 1,
+ "is_default": 0,
+ "is_standard": 0,
"label": "Manufacturing",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Production",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -29,6 +34,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Work Order",
+ "link_count": 0,
"link_to": "Work Order",
"link_type": "DocType",
"onboard": 1,
@@ -39,6 +45,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Production Plan",
+ "link_count": 0,
"link_to": "Production Plan",
"link_type": "DocType",
"onboard": 1,
@@ -49,6 +56,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Stock Entry",
+ "link_count": 0,
"link_to": "Stock Entry",
"link_type": "DocType",
"onboard": 1,
@@ -59,6 +67,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Job Card",
+ "link_count": 0,
"link_to": "Job Card",
"link_type": "DocType",
"onboard": 0,
@@ -69,6 +78,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Downtime Entry",
+ "link_count": 0,
"link_to": "Downtime Entry",
"link_type": "DocType",
"onboard": 0,
@@ -78,6 +88,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Bill of Materials",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -86,6 +97,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item",
+ "link_count": 0,
"link_to": "Item",
"link_type": "DocType",
"onboard": 1,
@@ -96,6 +108,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Bill of Materials",
+ "link_count": 0,
"link_to": "BOM",
"link_type": "DocType",
"onboard": 1,
@@ -106,6 +119,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Workstation",
+ "link_count": 0,
"link_to": "Workstation",
"link_type": "DocType",
"onboard": 0,
@@ -116,6 +130,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Operation",
+ "link_count": 0,
"link_to": "Operation",
"link_type": "DocType",
"onboard": 0,
@@ -126,6 +141,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Routing",
+ "link_count": 0,
"link_to": "Routing",
"link_type": "DocType",
"onboard": 0,
@@ -135,6 +151,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -143,6 +160,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Production Planning Report",
+ "link_count": 0,
"link_to": "Production Planning Report",
"link_type": "Report",
"onboard": 0,
@@ -153,6 +171,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Work Order Summary",
+ "link_count": 0,
"link_to": "Work Order Summary",
"link_type": "Report",
"onboard": 0,
@@ -163,6 +182,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Quality Inspection Summary",
+ "link_count": 0,
"link_to": "Quality Inspection Summary",
"link_type": "Report",
"onboard": 0,
@@ -173,6 +193,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Downtime Analysis",
+ "link_count": 0,
"link_to": "Downtime Analysis",
"link_type": "Report",
"onboard": 0,
@@ -183,6 +204,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Job Card Summary",
+ "link_count": 0,
"link_to": "Job Card Summary",
"link_type": "Report",
"onboard": 0,
@@ -193,6 +215,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "BOM Search",
+ "link_count": 0,
"link_to": "BOM Search",
"link_type": "Report",
"onboard": 0,
@@ -203,6 +226,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "BOM Stock Report",
+ "link_count": 0,
"link_to": "BOM Stock Report",
"link_type": "Report",
"onboard": 0,
@@ -213,6 +237,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Production Analytics",
+ "link_count": 0,
"link_to": "Production Analytics",
"link_type": "Report",
"onboard": 0,
@@ -223,6 +248,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "BOM Operations Time",
+ "link_count": 0,
"link_to": "BOM Operations Time",
"link_type": "Report",
"onboard": 0,
@@ -232,6 +258,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Tools",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -240,6 +267,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "BOM Update Tool",
+ "link_count": 0,
"link_to": "BOM Update Tool",
"link_type": "DocType",
"onboard": 0,
@@ -250,6 +278,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "BOM Comparison Tool",
+ "link_count": 0,
"link_to": "bom-comparison-tool",
"link_type": "Page",
"onboard": 0,
@@ -259,6 +288,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Settings",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -267,21 +297,26 @@
"hidden": 0,
"is_query_report": 0,
"label": "Manufacturing Settings",
+ "link_count": 0,
"link_to": "Manufacturing Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2020-12-01 13:38:39.365928",
+ "modified": "2021-08-05 12:16:00.825741",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Manufacturing",
"onboarding": "Manufacturing",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
"restrict_to_domain": "Manufacturing",
+ "roles": [],
+ "sequence_id": 17,
"shortcuts": [
{
"color": "Green",
@@ -346,5 +381,6 @@
"restrict_to_domain": "Manufacturing",
"type": "Dashboard"
}
- ]
+ ],
+ "title": "Manufacturing"
}
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/member/member.json b/erpnext/non_profit/doctype/member/member.json
index f190cfa..7c1baf1 100644
--- a/erpnext/non_profit/doctype/member/member.json
+++ b/erpnext/non_profit/doctype/member/member.json
@@ -26,7 +26,7 @@
"razorpay_details_section",
"subscription_id",
"customer_id",
- "subscription_activated",
+ "subscription_status",
"column_break_21",
"subscription_start",
"subscription_end"
@@ -152,12 +152,6 @@
"fieldtype": "Column Break"
},
{
- "default": "0",
- "fieldname": "subscription_activated",
- "fieldtype": "Check",
- "label": "Subscription Activated"
- },
- {
"fieldname": "subscription_start",
"fieldtype": "Date",
"label": "Subscription Start "
@@ -166,11 +160,17 @@
"fieldname": "subscription_end",
"fieldtype": "Date",
"label": "Subscription End"
+ },
+ {
+ "fieldname": "subscription_status",
+ "fieldtype": "Select",
+ "label": "Subscription Status",
+ "options": "\nActive\nHalted"
}
],
"image_field": "image",
"links": [],
- "modified": "2020-11-09 12:12:10.174647",
+ "modified": "2021-07-11 14:27:26.368039",
"modified_by": "Administrator",
"module": "Non Profit",
"name": "Member",
diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py
index 30be585..67828d6 100644
--- a/erpnext/non_profit/doctype/member/member.py
+++ b/erpnext/non_profit/doctype/member/member.py
@@ -84,7 +84,9 @@
"email_id": user_details.email,
"pan_number": user_details.pan or None,
"membership_type": user_details.plan_id,
- "subscription_id": user_details.subscription_id or None
+ "customer_id": user_details.customer_id or None,
+ "subscription_id": user_details.subscription_id or None,
+ "subscription_status": user_details.subscription_status or ""
})
member.insert(ignore_permissions=True)
diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py
index e8ae618..b584116 100644
--- a/erpnext/non_profit/doctype/membership/membership.py
+++ b/erpnext/non_profit/doctype/membership/membership.py
@@ -196,11 +196,14 @@
return invoice
-def get_member_based_on_subscription(subscription_id, email):
- members = frappe.get_all("Member", filters={
- "subscription_id": subscription_id,
- "email_id": email
- }, order_by="creation desc")
+def get_member_based_on_subscription(subscription_id, email=None, customer_id=None):
+ filters = {"subscription_id": subscription_id}
+ if email:
+ filters.update({"email_id": email})
+ if customer_id:
+ filters.update({"customer_id": customer_id})
+
+ members = frappe.get_all("Member", filters=filters, order_by="creation desc")
try:
return frappe.get_doc("Member", members[0]["name"])
@@ -209,8 +212,6 @@
def verify_signature(data, endpoint="Membership"):
- if frappe.flags.in_test or os.environ.get("CI"):
- return True
signature = frappe.request.headers.get("X-Razorpay-Signature")
settings = frappe.get_doc("Non Profit Settings")
@@ -225,16 +226,7 @@
@frappe.whitelist(allow_guest=True)
def trigger_razorpay_subscription(*args, **kwargs):
data = frappe.request.get_data(as_text=True)
- try:
- verify_signature(data)
- except Exception as e:
- log = frappe.log_error(e, "Membership Webhook Verification Error")
- notify_failure(log)
- return { "status": "Failed", "reason": e}
-
- if isinstance(data, six.string_types):
- data = json.loads(data)
- data = frappe._dict(data)
+ data = process_request_data(data)
subscription = data.payload.get("subscription", {}).get("entity", {})
subscription = frappe._dict(subscription)
@@ -281,7 +273,7 @@
# Update membership values
member.subscription_start = datetime.fromtimestamp(subscription.start_at)
member.subscription_end = datetime.fromtimestamp(subscription.end_at)
- member.subscription_activated = 1
+ member.subscription_status = "Active"
member.flags.ignore_mandatory = True
member.save()
@@ -294,9 +286,67 @@
message = "{0}\n\n{1}\n\n{2}: {3}".format(e, frappe.get_traceback(), _("Payment ID"), payment.id)
log = frappe.log_error(message, _("Error creating membership entry for {0}").format(member.name))
notify_failure(log)
- return { "status": "Failed", "reason": e}
+ return {"status": "Failed", "reason": e}
- return { "status": "Success" }
+ return {"status": "Success"}
+
+
+@frappe.whitelist(allow_guest=True)
+def update_halted_razorpay_subscription(*args, **kwargs):
+ """
+ When all retries have been exhausted, Razorpay moves the subscription to the halted state.
+ The customer has to manually retry the charge or change the card linked to the subscription,
+ for the subscription to move back to the active state.
+ """
+ if frappe.request:
+ data = frappe.request.get_data(as_text=True)
+ data = process_request_data(data)
+ elif frappe.flags.in_test:
+ data = kwargs.get("data")
+ data = frappe._dict(data)
+ else:
+ return
+
+ if not data.event == "subscription.halted":
+ return
+
+ subscription = data.payload.get("subscription", {}).get("entity", {})
+ subscription = frappe._dict(subscription)
+
+ try:
+ member = get_member_based_on_subscription(subscription.id, customer_id=subscription.customer_id)
+ if not member:
+ frappe.throw(_("Member with Razorpay Subscription ID {0} not found").format(subscription.id))
+
+ member.subscription_status = "Halted"
+ member.flags.ignore_mandatory = True
+ member.save()
+
+ if subscription.get("notes"):
+ member = get_additional_notes(member, subscription)
+
+ except Exception as e:
+ message = "{0}\n\n{1}".format(e, frappe.get_traceback())
+ log = frappe.log_error(message, _("Error updating halted status for member {0}").format(member.name))
+ notify_failure(log)
+ return {"status": "Failed", "reason": e}
+
+ return {"status": "Success"}
+
+
+def process_request_data(data):
+ try:
+ verify_signature(data)
+ except Exception as e:
+ log = frappe.log_error(e, "Membership Webhook Verification Error")
+ notify_failure(log)
+ return {"status": "Failed", "reason": e}
+
+ if isinstance(data, six.string_types):
+ data = json.loads(data)
+ data = frappe._dict(data)
+
+ return data
def get_company_for_memberships():
@@ -362,4 +412,4 @@
`tabMembership` SET `status` = 'Expired'
WHERE
`status` not in ('Cancelled') AND `to_date` < %s
- """, (nowdate()))
\ No newline at end of file
+ """, (nowdate()))
diff --git a/erpnext/non_profit/doctype/membership/test_membership.py b/erpnext/non_profit/doctype/membership/test_membership.py
index 31da792..0f5a9be 100644
--- a/erpnext/non_profit/doctype/membership/test_membership.py
+++ b/erpnext/non_profit/doctype/membership/test_membership.py
@@ -6,6 +6,7 @@
import frappe
import erpnext
from erpnext.non_profit.doctype.member.member import create_member
+from erpnext.non_profit.doctype.membership.membership import update_halted_razorpay_subscription
from frappe.utils import nowdate, add_months
class TestMembership(unittest.TestCase):
@@ -13,11 +14,16 @@
plan = setup_membership()
# make test member
- self.member_doc = create_member(frappe._dict({
- 'fullname': "_Test_Member",
- 'email': "_test_member_erpnext@example.com",
- 'plan_id': plan.name
- }))
+ self.member_doc = create_member(
+ frappe._dict({
+ "fullname": "_Test_Member",
+ "email": "_test_member_erpnext@example.com",
+ "plan_id": plan.name,
+ "subscription_id": "sub_DEX6xcJ1HSW4CR",
+ "customer_id": "cust_C0WlbKhp3aLA7W",
+ "subscription_status": "Active"
+ })
+ )
self.member_doc.make_customer_and_link()
self.member = self.member_doc.name
@@ -51,6 +57,20 @@
"to_date": add_months(nowdate(), 3),
})
+ def test_halted_memberships(self):
+ make_membership(self.member, {
+ "from_date": add_months(nowdate(), 2),
+ "to_date": add_months(nowdate(), 3)
+ })
+
+ self.assertEqual(frappe.db.get_value("Member", self.member, "subscription_status"), "Active")
+ payload = get_subscription_payload()
+ update_halted_razorpay_subscription(data=payload)
+ self.assertEqual(frappe.db.get_value("Member", self.member, "subscription_status"), "Halted")
+
+ def tearDown(self):
+ frappe.db.rollback()
+
def set_config(key, value):
frappe.db.set_value("Non Profit Settings", None, key, value)
@@ -115,4 +135,28 @@
else:
plan = frappe.get_doc("Membership Type", "_rzpy_test_milythm")
- return plan
\ No newline at end of file
+ return plan
+
+def get_subscription_payload():
+ return {
+ "entity": "event",
+ "account_id": "acc_BFQ7uQEaa7j2z7",
+ "event": "subscription.halted",
+ "contains": [
+ "subscription"
+ ],
+ "payload": {
+ "subscription": {
+ "entity": {
+ "id": "sub_DEX6xcJ1HSW4CR",
+ "entity": "subscription",
+ "plan_id": "_rzpy_test_milythm",
+ "customer_id": "cust_C0WlbKhp3aLA7W",
+ "status": "halted",
+ "notes": {
+ "Important": "Notes for Internal Reference"
+ },
+ }
+ }
+ }
+ }
\ No newline at end of file
diff --git a/erpnext/non_profit/workspace/non_profit/non_profit.json b/erpnext/non_profit/workspace/non_profit/non_profit.json
index 2557d77..e6d4445 100644
--- a/erpnext/non_profit/workspace/non_profit/non_profit.json
+++ b/erpnext/non_profit/workspace/non_profit/non_profit.json
@@ -1,23 +1,27 @@
{
- "category": "Domains",
+ "category": "",
"charts": [],
+ "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Member\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Non Profit Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Membership\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Chapter\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Chapter Member\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Grant Application\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Membership\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Volunteer\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Chapter\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Donation\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tax Exemption Certification (India)\", \"col\": 4}}]",
"creation": "2020-03-02 17:23:47.811421",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "non-profit",
"idx": 0,
"is_default": 0,
- "is_standard": 1,
+ "is_standard": 0,
"label": "Non Profit",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Loan Management",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -26,6 +30,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Type",
+ "link_count": 0,
"link_to": "Loan Type",
"link_type": "DocType",
"onboard": 0,
@@ -36,6 +41,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan Application",
+ "link_count": 0,
"link_to": "Loan Application",
"link_type": "DocType",
"onboard": 0,
@@ -46,6 +52,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loan",
+ "link_count": 0,
"link_to": "Loan",
"link_type": "DocType",
"onboard": 0,
@@ -55,6 +62,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Grant Application",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -63,6 +71,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Grant Application",
+ "link_count": 0,
"link_to": "Grant Application",
"link_type": "DocType",
"onboard": 0,
@@ -72,6 +81,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Membership",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -80,6 +90,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Member",
+ "link_count": 0,
"link_to": "Member",
"link_type": "DocType",
"onboard": 1,
@@ -90,6 +101,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Membership",
+ "link_count": 0,
"link_to": "Membership",
"link_type": "DocType",
"onboard": 1,
@@ -100,6 +112,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Membership Type",
+ "link_count": 0,
"link_to": "Membership Type",
"link_type": "DocType",
"onboard": 0,
@@ -110,6 +123,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Membership Settings",
+ "link_count": 0,
"link_to": "Non Profit Settings",
"link_type": "DocType",
"onboard": 0,
@@ -119,6 +133,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Volunteer",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -127,6 +142,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Volunteer",
+ "link_count": 0,
"link_to": "Volunteer",
"link_type": "DocType",
"onboard": 1,
@@ -137,6 +153,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Volunteer Type",
+ "link_count": 0,
"link_to": "Volunteer Type",
"link_type": "DocType",
"onboard": 0,
@@ -146,6 +163,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Chapter",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -154,6 +172,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Chapter",
+ "link_count": 0,
"link_to": "Chapter",
"link_type": "DocType",
"onboard": 1,
@@ -163,6 +182,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Donation",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -171,6 +191,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Donor",
+ "link_count": 0,
"link_to": "Donor",
"link_type": "DocType",
"onboard": 0,
@@ -181,6 +202,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Donor Type",
+ "link_count": 0,
"link_to": "Donor Type",
"link_type": "DocType",
"onboard": 0,
@@ -190,6 +212,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Donation",
+ "link_count": 0,
"link_to": "Donation",
"link_type": "DocType",
"onboard": 0,
@@ -199,6 +222,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Tax Exemption Certification (India)",
+ "link_count": 0,
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
@@ -207,20 +231,26 @@
"hidden": 0,
"is_query_report": 0,
"label": "Tax Exemption 80G Certificate",
+ "link_count": 0,
"link_to": "Tax Exemption 80G Certificate",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2021-03-11 11:38:09.140655",
+ "modified": "2021-08-05 12:16:01.146206",
"modified_by": "Administrator",
"module": "Non Profit",
"name": "Non Profit",
+ "onboarding": "",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
"restrict_to_domain": "Non Profit",
+ "roles": [],
+ "sequence_id": 18,
"shortcuts": [
{
"label": "Member",
@@ -247,5 +277,6 @@
"link_to": "Chapter Member",
"type": "DocType"
}
- ]
+ ],
+ "title": "Non Profit"
}
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 0ae8130..86356e3 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -45,7 +45,6 @@
erpnext.patches.v11_0.make_asset_finance_book_against_old_entries
erpnext.patches.v11_0.check_buying_selling_in_currency_exchange
erpnext.patches.v11_0.move_item_defaults_to_child_table_for_multicompany #02-07-2018 #19-06-2019
-erpnext.patches.v11_0.refactor_erpnext_shopify #2018-09-07
erpnext.patches.v11_0.rename_overproduction_percent_field
erpnext.patches.v11_0.update_backflush_subcontract_rm_based_on_bom
erpnext.patches.v11_0.inter_state_field_for_gst
@@ -143,7 +142,6 @@
erpnext.patches.v12_0.rename_bank_account_field_in_journal_entry_account
erpnext.patches.v12_0.create_default_energy_point_rules
erpnext.patches.v12_0.set_produced_qty_field_in_sales_order_for_work_order
-erpnext.patches.v12_0.set_default_shopify_app_type
erpnext.patches.v12_0.set_cwip_and_delete_asset_settings
erpnext.patches.v12_0.set_expense_account_in_landed_cost_voucher_taxes
erpnext.patches.v12_0.replace_accounting_with_accounts_in_home_settings
@@ -244,7 +242,6 @@
erpnext.patches.v13_0.update_reason_for_resignation_in_employee
execute:frappe.delete_doc("Report", "Quoted Item Comparison")
erpnext.patches.v13_0.update_member_email_address
-erpnext.patches.v13_0.update_custom_fields_for_shopify
erpnext.patches.v13_0.updates_for_multi_currency_payroll
erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy
erpnext.patches.v13_0.update_pos_closing_entry_in_merge_log
@@ -295,3 +292,11 @@
erpnext.patches.v13_0.update_job_card_details
erpnext.patches.v13_0.update_level_in_bom #1234sswef
erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry
+erpnext.patches.v13_0.update_subscription_status_in_memberships
+erpnext.patches.v13_0.update_amt_in_work_order_required_items
+erpnext.patches.v12_0.show_einvoice_irn_cancelled_field
+erpnext.patches.v13_0.delete_orphaned_tables
+erpnext.patches.v13_0.update_export_type_for_gst
+erpnext.patches.v13_0.update_tds_check_field #3
+erpnext.patches.v13_0.add_custom_field_for_south_africa #2
+erpnext.patches.v13_0.shopify_deprecation_warning
diff --git a/erpnext/patches/v11_0/refactor_erpnext_shopify.py b/erpnext/patches/v11_0/refactor_erpnext_shopify.py
deleted file mode 100644
index 340e9fc..0000000
--- a/erpnext/patches/v11_0/refactor_erpnext_shopify.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.installer import remove_from_installed_apps
-
-def execute():
- frappe.reload_doc('erpnext_integrations', 'doctype', 'shopify_settings')
- frappe.reload_doc('erpnext_integrations', 'doctype', 'shopify_tax_account')
- frappe.reload_doc('erpnext_integrations', 'doctype', 'shopify_log')
- frappe.reload_doc('erpnext_integrations', 'doctype', 'shopify_webhook_detail')
-
- if 'erpnext_shopify' in frappe.get_installed_apps():
- remove_from_installed_apps('erpnext_shopify')
-
- frappe.delete_doc("Module Def", 'erpnext_shopify')
-
- frappe.db.commit()
-
- frappe.db.sql("truncate `tabShopify Log`")
-
- setup_app_type()
- else:
- disable_shopify()
-
-def setup_app_type():
- try:
- shopify_settings = frappe.get_doc("Shopify Settings")
- shopify_settings.app_type = 'Private'
- shopify_settings.update_price_in_erpnext_price_list = 0 if getattr(shopify_settings, 'push_prices_to_shopify', None) else 1
- shopify_settings.flags.ignore_mandatory = True
- shopify_settings.ignore_permissions = True
- shopify_settings.save()
- except Exception:
- frappe.db.set_value("Shopify Settings", None, "enable_shopify", 0)
- frappe.log_error(frappe.get_traceback())
-
-def disable_shopify():
- # due to frappe.db.set_value wrongly written and enable_shopify being default 1
- # Shopify Settings isn't properly configured and leads to error
- shopify = frappe.get_doc('Shopify Settings')
-
- if shopify.app_type == "Public" or shopify.app_type == None or \
- (shopify.enable_shopify and not (shopify.shopify_url or shopify.api_key)):
- frappe.db.set_value("Shopify Settings", None, "enable_shopify", 0)
diff --git a/erpnext/patches/v12_0/set_default_shopify_app_type.py b/erpnext/patches/v12_0/set_default_shopify_app_type.py
deleted file mode 100644
index d040ea7..0000000
--- a/erpnext/patches/v12_0/set_default_shopify_app_type.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
- frappe.reload_doc('erpnext_integrations', 'doctype', 'shopify_settings')
- frappe.db.set_value('Shopify Settings', None, 'app_type', 'Private')
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py b/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py
new file mode 100644
index 0000000..2319c17
--- /dev/null
+++ b/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py
@@ -0,0 +1,12 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ company = frappe.get_all('Company', filters = {'country': 'India'})
+ if not company:
+ return
+
+ irn_cancelled_field = frappe.db.exists('Custom Field', {'dt': 'Sales Invoice', 'fieldname': 'irn_cancelled'})
+ if irn_cancelled_field:
+ frappe.db.set_value('Custom Field', irn_cancelled_field, 'depends_on', 'eval: doc.irn')
+ frappe.db.set_value('Custom Field', irn_cancelled_field, 'read_only', 0)
diff --git a/erpnext/patches/v13_0/add_custom_field_for_south_africa.py b/erpnext/patches/v13_0/add_custom_field_for_south_africa.py
new file mode 100644
index 0000000..73ff1ca
--- /dev/null
+++ b/erpnext/patches/v13_0/add_custom_field_for_south_africa.py
@@ -0,0 +1,14 @@
+# Copyright (c) 2020, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from erpnext.regional.south_africa.setup import make_custom_fields, add_permissions
+
+def execute():
+ company = frappe.get_all('Company', filters = {'country': 'South Africa'})
+ if not company:
+ return
+
+ make_custom_fields()
+ add_permissions()
diff --git a/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py b/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py
index 48999e6..0d8109c 100644
--- a/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py
+++ b/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py
@@ -10,6 +10,7 @@
if not frappe.db.has_column('Work Order', 'has_batch_no'):
return
+ frappe.reload_doc('manufacturing', 'doctype', 'manufacturing_settings')
if cint(frappe.db.get_single_value('Manufacturing Settings', 'make_serial_no_batch_from_work_order')):
return
@@ -29,19 +30,20 @@
return
repost_stock_entries = []
+
stock_entries = frappe.db.sql_list('''
SELECT
se.name
FROM
`tabStock Entry` se
WHERE
- se.purpose = 'Manufacture' and se.docstatus < 2 and se.work_order in {work_orders}
+ se.purpose = 'Manufacture' and se.docstatus < 2 and se.work_order in %s
and not exists(
select name from `tabStock Entry Detail` sed where sed.parent = se.name and sed.is_finished_item = 1
)
- Order BY
+ ORDER BY
se.posting_date, se.posting_time
- '''.format(work_orders=tuple(work_orders)))
+ ''', (work_orders,))
if stock_entries:
print('Length of stock entries', len(stock_entries))
@@ -107,4 +109,4 @@
"company": doc.company
})
- create_repost_item_valuation_entry(args)
\ No newline at end of file
+ create_repost_item_valuation_entry(args)
diff --git a/erpnext/patches/v13_0/delete_orphaned_tables.py b/erpnext/patches/v13_0/delete_orphaned_tables.py
new file mode 100644
index 0000000..1d6eebe
--- /dev/null
+++ b/erpnext/patches/v13_0/delete_orphaned_tables.py
@@ -0,0 +1,69 @@
+# Copyright (c) 2019, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+from frappe.utils import getdate
+
+def execute():
+ frappe.reload_doc('setup', 'doctype', 'transaction_deletion_record')
+
+ if has_deleted_company_transactions():
+ child_doctypes = get_child_doctypes_whose_parent_doctypes_were_affected()
+
+ for doctype in child_doctypes:
+ docs = frappe.get_all(doctype, fields=['name', 'parent', 'parenttype', 'creation'])
+
+ for doc in docs:
+ if not frappe.db.exists(doc['parenttype'], doc['parent']):
+ frappe.db.delete(doctype, {'name': doc['name']})
+
+ elif check_for_new_doc_with_same_name_as_deleted_parent(doc):
+ frappe.db.delete(doctype, {'name': doc['name']})
+
+def has_deleted_company_transactions():
+ return frappe.get_all('Transaction Deletion Record')
+
+def get_child_doctypes_whose_parent_doctypes_were_affected():
+ parent_doctypes = get_affected_doctypes()
+ child_doctypes = frappe.get_all(
+ 'DocField',
+ filters={
+ 'fieldtype': 'Table',
+ 'parent':['in', parent_doctypes]
+ }, pluck='options')
+
+ return child_doctypes
+
+def get_affected_doctypes():
+ affected_doctypes = []
+ tdr_docs = frappe.get_all('Transaction Deletion Record', pluck="name")
+
+ for tdr in tdr_docs:
+ tdr_doc = frappe.get_doc("Transaction Deletion Record", tdr)
+
+ for doctype in tdr_doc.doctypes:
+ if is_not_child_table(doctype.doctype_name):
+ affected_doctypes.append(doctype.doctype_name)
+
+ affected_doctypes = remove_duplicate_items(affected_doctypes)
+ return affected_doctypes
+
+def is_not_child_table(doctype):
+ return not bool(frappe.get_value('DocType', doctype, 'istable'))
+
+def remove_duplicate_items(affected_doctypes):
+ return list(set(affected_doctypes))
+
+def check_for_new_doc_with_same_name_as_deleted_parent(doc):
+ """
+ Compares creation times of parent and child docs.
+ Since Transaction Deletion Record resets the naming series after deletion,
+ it allows the creation of new docs with the same names as the deleted ones.
+ """
+
+ parent_creation_time = frappe.db.get_value(doc['parenttype'], doc['parent'], 'creation')
+ child_creation_time = doc['creation']
+
+ return getdate(parent_creation_time) > getdate(child_creation_time)
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/rename_issue_doctype_fields.py b/erpnext/patches/v13_0/rename_issue_doctype_fields.py
index fa1dfed..41c51c3 100644
--- a/erpnext/patches/v13_0/rename_issue_doctype_fields.py
+++ b/erpnext/patches/v13_0/rename_issue_doctype_fields.py
@@ -37,7 +37,7 @@
if frappe.db.exists('DocType', 'Opportunity'):
opportunities = frappe.db.get_all('Opportunity', fields=['name', 'mins_to_first_response'], order_by='creation desc')
- frappe.reload_doc('crm', 'doctype', 'opportunity')
+ frappe.reload_doctype('Opportunity', force=True)
rename_field('Opportunity', 'mins_to_first_response', 'first_response_time')
# change fieldtype to duration
diff --git a/erpnext/patches/v13_0/shopify_deprecation_warning.py b/erpnext/patches/v13_0/shopify_deprecation_warning.py
new file mode 100644
index 0000000..245d1a9
--- /dev/null
+++ b/erpnext/patches/v13_0/shopify_deprecation_warning.py
@@ -0,0 +1,10 @@
+import click
+
+
+def execute():
+
+ click.secho(
+ "Shopify Integration is moved to a separate app and will be removed from ERPNext in version-14.\n"
+ "Please install the app to continue using the integration: https://github.com/frappe/ecommerce_integrations",
+ fg="yellow",
+ )
diff --git a/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py
new file mode 100644
index 0000000..eae5ff6
--- /dev/null
+++ b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py
@@ -0,0 +1,10 @@
+import frappe
+
+def execute():
+ """ Correct amount in child table of required items table."""
+
+ frappe.reload_doc("manufacturing", "doctype", "work_order")
+ frappe.reload_doc("manufacturing", "doctype", "work_order_item")
+
+ frappe.db.sql("""UPDATE `tabWork Order Item` SET amount = rate * required_qty""")
+
diff --git a/erpnext/patches/v13_0/update_custom_fields_for_shopify.py b/erpnext/patches/v13_0/update_custom_fields_for_shopify.py
deleted file mode 100644
index f1d2ea2..0000000
--- a/erpnext/patches/v13_0/update_custom_fields_for_shopify.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import setup_custom_fields
-
-def execute():
- if frappe.db.get_single_value('Shopify Settings', 'enable_shopify'):
- setup_custom_fields()
diff --git a/erpnext/patches/v13_0/update_export_type_for_gst.py b/erpnext/patches/v13_0/update_export_type_for_gst.py
new file mode 100644
index 0000000..478a2a6
--- /dev/null
+++ b/erpnext/patches/v13_0/update_export_type_for_gst.py
@@ -0,0 +1,24 @@
+import frappe
+
+def execute():
+ company = frappe.get_all('Company', filters = {'country': 'India'})
+ if not company:
+ return
+
+ # Update custom fields
+ fieldname = frappe.db.get_value('Custom Field', {'dt': 'Customer', 'fieldname': 'export_type'})
+ if fieldname:
+ frappe.db.set_value('Custom Field', fieldname, 'default', '')
+
+ fieldname = frappe.db.get_value('Custom Field', {'dt': 'Supplier', 'fieldname': 'export_type'})
+ if fieldname:
+ frappe.db.set_value('Custom Field', fieldname, 'default', '')
+
+ # Update Customer/Supplier Masters
+ frappe.db.sql("""
+ UPDATE `tabCustomer` set export_type = '' WHERE gst_category NOT IN ('SEZ', 'Overseas', 'Deemed Export')
+ """)
+
+ frappe.db.sql("""
+ UPDATE `tabSupplier` set export_type = '' WHERE gst_category NOT IN ('SEZ', 'Overseas')
+ """)
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/update_subscription_status_in_memberships.py b/erpnext/patches/v13_0/update_subscription_status_in_memberships.py
new file mode 100644
index 0000000..28e650e
--- /dev/null
+++ b/erpnext/patches/v13_0/update_subscription_status_in_memberships.py
@@ -0,0 +1,9 @@
+import frappe
+
+def execute():
+ if frappe.db.exists('DocType', 'Member'):
+ frappe.reload_doc('Non Profit', 'doctype', 'Member')
+
+ if frappe.db.has_column('Member', 'subscription_activated'):
+ frappe.db.sql('UPDATE `tabMember` SET subscription_status = "Active" WHERE subscription_activated = 1')
+ frappe.db.sql_ddl('ALTER table `tabMember` DROP COLUMN subscription_activated')
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/update_tds_check_field.py b/erpnext/patches/v13_0/update_tds_check_field.py
new file mode 100644
index 0000000..3d14958
--- /dev/null
+++ b/erpnext/patches/v13_0/update_tds_check_field.py
@@ -0,0 +1,9 @@
+import frappe
+
+def execute():
+ if frappe.db.has_table("Tax Withholding Category") \
+ and frappe.db.has_column("Tax Withholding Category", "round_off_tax_amount"):
+ frappe.db.sql("""
+ UPDATE `tabTax Withholding Category` set round_off_tax_amount = 0
+ WHERE round_off_tax_amount IS NULL
+ """)
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py
index ebeddf9..b978cbe 100644
--- a/erpnext/payroll/doctype/additional_salary/additional_salary.py
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py
@@ -7,6 +7,7 @@
from frappe.model.document import Document
from frappe import _, bold
from frappe.utils import getdate, date_diff, comma_and, formatdate
+from erpnext.hr.utils import validate_active_employee
class AdditionalSalary(Document):
def on_submit(self):
@@ -19,6 +20,7 @@
self.update_employee_referral(cancel=True)
def validate(self):
+ validate_active_employee(self.employee)
self.validate_dates()
self.validate_salary_structure()
self.validate_recurring_additional_salary_overlap()
@@ -110,11 +112,11 @@
no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1
return amount_per_day * no_of_days
+@frappe.whitelist()
def get_additional_salaries(employee, start_date, end_date, component_type):
additional_salary_list = frappe.db.sql("""
- select name, salary_component as component, type, amount,
- overwrite_salary_structure_amount as overwrite,
- deduct_full_tax_on_selected_payroll_date
+ select name, salary_component as component, type, amount, overwrite_salary_structure_amount as overwrite,
+ deduct_full_tax_on_selected_payroll_date, is_recurring
from `tabAdditional Salary`
where employee=%(employee)s
and docstatus = 1
diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py
index 27df30a..5ebe514 100644
--- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py
+++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py
@@ -9,10 +9,11 @@
from frappe.model.document import Document
from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor
from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
-from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount
+from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount, validate_active_employee
class EmployeeBenefitApplication(Document):
def validate(self):
+ validate_active_employee(self.employee)
self.validate_duplicate_on_payroll_period()
if not self.max_benefits:
self.max_benefits = get_max_benefits_remaining(self.employee, self.date, self.payroll_period)
diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
index d9937a7..c6713f3 100644
--- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
+++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
@@ -8,12 +8,13 @@
from frappe.utils import flt
from frappe.model.document import Document
from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits
-from erpnext.hr.utils import get_previous_claimed_amount
+from erpnext.hr.utils import get_previous_claimed_amount, validate_active_employee
from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period
from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
class EmployeeBenefitClaim(Document):
def validate(self):
+ validate_active_employee(self.employee)
max_benefits = get_max_benefits(self.employee, self.claim_date)
if not max_benefits or max_benefits <= 0:
frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee))
diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py
index ead3db1..6b918ba 100644
--- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py
+++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py
@@ -6,9 +6,11 @@
import frappe
from frappe import _
from frappe.model.document import Document
+from erpnext.hr.utils import validate_active_employee
class EmployeeIncentive(Document):
def validate(self):
+ validate_active_employee(self.employee)
self.validate_salary_structure()
def validate_salary_structure(self):
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py
index fb71a28..e11d60a 100644
--- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py
+++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py
@@ -8,11 +8,12 @@
from frappe import _
from frappe.utils import flt
from frappe.model.mapper import get_mapped_doc
-from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \
+from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, validate_active_employee, \
calculate_annual_eligible_hra_exemption, validate_duplicate_exemption_for_payroll_period
class EmployeeTaxExemptionDeclaration(Document):
def validate(self):
+ validate_active_employee(self.employee)
validate_tax_declaration(self.declarations)
validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee)
self.set_total_declared_amount()
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py
index 5bc33a6..8131ae0 100644
--- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py
+++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py
@@ -7,11 +7,12 @@
from frappe.model.document import Document
from frappe import _
from frappe.utils import flt
-from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \
+from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, validate_active_employee, \
calculate_hra_exemption_for_period, validate_duplicate_exemption_for_payroll_period
class EmployeeTaxExemptionProofSubmission(Document):
def validate(self):
+ validate_active_employee(self.employee)
validate_tax_declaration(self.tax_exemption_proofs)
self.set_total_actual_amount()
self.set_total_exemption_amount()
diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
index 049ea26..055bea7 100644
--- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
+++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
@@ -7,11 +7,10 @@
from frappe.model.document import Document
from frappe import _
from frappe.utils import getdate
-
+from erpnext.hr.utils import validate_active_employee
class RetentionBonus(Document):
def validate(self):
- if frappe.get_value('Employee', self.employee, 'status') != 'Active':
- frappe.throw(_('Cannot create Retention Bonus for Left or Inactive Employees'))
+ validate_active_employee(self.employee)
if getdate(self.bonus_payment_date) < getdate():
frappe.throw(_('Bonus Payment Date cannot be a past date'))
diff --git a/erpnext/payroll/doctype/salary_component/salary_component.js b/erpnext/payroll/doctype/salary_component/salary_component.js
index dbf7514..e9e6f81 100644
--- a/erpnext/payroll/doctype/salary_component/salary_component.js
+++ b/erpnext/payroll/doctype/salary_component/salary_component.js
@@ -4,11 +4,18 @@
frappe.ui.form.on('Salary Component', {
setup: function(frm) {
frm.set_query("account", "accounts", function(doc, cdt, cdn) {
- var d = locals[cdt][cdn];
+ let d = frappe.get_doc(cdt, cdn);
+
+ let root_type = "Liability";
+ if (frm.doc.type == "Deduction") {
+ root_type = "Expense";
+ }
+
return {
filters: {
"is_group": 0,
- "company": d.company
+ "company": d.company,
+ "root_type": root_type
}
};
});
diff --git a/erpnext/payroll/doctype/salary_detail/salary_detail.json b/erpnext/payroll/doctype/salary_detail/salary_detail.json
index 393f647..97608d7 100644
--- a/erpnext/payroll/doctype/salary_detail/salary_detail.json
+++ b/erpnext/payroll/doctype/salary_detail/salary_detail.json
@@ -12,6 +12,7 @@
"year_to_date",
"section_break_5",
"additional_salary",
+ "is_recurring_additional_salary",
"statistical_component",
"depends_on_payment_days",
"exempted_from_income_tax",
@@ -235,11 +236,19 @@
"label": "Year To Date",
"options": "currency",
"read_only": 1
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='earnings' && doc.additional_salary",
+ "fieldname": "is_recurring_additional_salary",
+ "fieldtype": "Check",
+ "label": "Is Recurring Additional Salary",
+ "read_only": 1
}
],
"istable": 1,
"links": [],
- "modified": "2021-01-14 13:39:15.847158",
+ "modified": "2021-03-14 13:39:15.847158",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Detail",
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index f82b0d5..f0ca64f 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -7,18 +7,19 @@
from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words, formatdate, get_first_day
from frappe.model.naming import make_autoname
+from frappe.utils.background_jobs import enqueue
from frappe import msgprint, _
from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
from erpnext.utilities.transaction_base import TransactionBase
-from frappe.utils.background_jobs import enqueue
from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salaries
from erpnext.payroll.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period
from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount
from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits
from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts, create_repayment_entry
from erpnext.accounts.utils import get_fiscal_year
+from erpnext.hr.utils import validate_active_employee
from six import iteritems
class SalarySlip(TransactionBase):
@@ -39,6 +40,7 @@
def validate(self):
self.status = self.get_status()
+ validate_active_employee(self.employee)
self.validate_dates()
self.check_existing()
if not self.salary_slip_based_on_timesheet:
@@ -616,7 +618,8 @@
get_salary_component_data(additional_salary.component),
additional_salary.amount,
component_type,
- additional_salary
+ additional_salary,
+ is_recurring = additional_salary.is_recurring
)
def add_tax_components(self, payroll_period):
@@ -637,17 +640,20 @@
tax_row = get_salary_component_data(d)
self.update_component_row(tax_row, tax_amount, "deductions")
- def update_component_row(self, component_data, amount, component_type, additional_salary=None):
+ def update_component_row(self, component_data, amount, component_type, additional_salary=None, is_recurring = 0):
component_row = None
for d in self.get(component_type):
if d.salary_component != component_data.salary_component:
continue
if (
- (not d.additional_salary
- and (not additional_salary or additional_salary.overwrite))
- or (additional_salary
- and additional_salary.name == d.additional_salary)
+ (
+ not d.additional_salary
+ and (not additional_salary or additional_salary.overwrite)
+ ) or (
+ additional_salary
+ and additional_salary.name == d.additional_salary
+ )
):
component_row = d
break
@@ -675,8 +681,13 @@
component_row.set(attr, component_data.get(attr))
if additional_salary:
- component_row.default_amount = 0
- component_row.additional_amount = amount
+ component_row.is_recurring_additional_salary = is_recurring
+ if additional_salary.overwrite:
+ component_row.additional_amount = flt(flt(amount) - flt(component_row.get("default_amount", 0)),
+ component_row.precision("additional_amount"))
+ else:
+ component_row.default_amount = 0
+ component_row.additional_amount = amount
component_row.additional_salary = additional_salary.name
component_row.deduct_full_tax_on_selected_payroll_date = \
additional_salary.deduct_full_tax_on_selected_payroll_date
@@ -708,6 +719,7 @@
# get remaining numbers of sub-period (period for which one salary is processed)
remaining_sub_periods = get_period_factor(self.employee,
self.start_date, self.end_date, self.payroll_frequency, payroll_period)[1]
+
# get taxable_earnings, paid_taxes for previous period
previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(payroll_period.start_date,
self.start_date, tax_slab.allow_tax_exemption)
@@ -867,8 +879,16 @@
if earning.is_tax_applicable:
if additional_amount:
- taxable_earnings += (amount - additional_amount)
- additional_income += additional_amount
+ if not earning.is_recurring_additional_salary:
+ taxable_earnings += (amount - additional_amount)
+ additional_income += additional_amount
+ else:
+ to_date = frappe.db.get_value("Additional Salary", earning.additional_salary, 'to_date')
+ period = (getdate(to_date).month - getdate(self.start_date).month) + 1
+ if period > 0:
+ taxable_earnings += (amount - additional_amount) * period
+ additional_income += additional_amount * period
+
if earning.deduct_full_tax_on_selected_payroll_date:
additional_income_with_full_tax += additional_amount
continue
diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
index 374dd7e..3957d83 100644
--- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
+++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
@@ -129,7 +129,7 @@
"earnings": make_earning_salary_component(setup=True, test_tax=test_tax, company_list=["_Test Company"]),
"deductions": make_deduction_salary_component(setup=True, test_tax=test_tax, company_list=["_Test Company"]),
"payroll_frequency": payroll_frequency,
- "payment_account": get_random("Account", filters={"account_currency": currency}),
+ "payment_account": get_random("Account", filters={'account_currency': currency}),
"currency": currency
}
if other_details and isinstance(other_details, dict):
diff --git a/erpnext/payroll/workspace/payroll/payroll.json b/erpnext/payroll/workspace/payroll/payroll.json
index 8149730..b55bdc7 100644
--- a/erpnext/payroll/workspace/payroll/payroll.json
+++ b/erpnext/payroll/workspace/payroll/payroll.json
@@ -1,27 +1,32 @@
{
- "category": "Modules",
+ "category": "",
"charts": [
{
"chart_name": "Outgoing Salary",
"label": "Outgoing Salary"
}
],
+ "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Payroll\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Outgoing Salary\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Salary Structure\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Payroll Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Salary Slip\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Income Tax Slab\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Salary Register\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Payroll\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Taxation\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Compensations\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
"creation": "2020-05-27 19:54:23.405607",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "money-coins-1",
"idx": 0,
- "is_standard": 1,
+ "is_default": 0,
+ "is_standard": 0,
"label": "Payroll",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Payroll",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -30,6 +35,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Salary Component",
+ "link_count": 0,
"link_to": "Salary Component",
"link_type": "DocType",
"onboard": 1,
@@ -40,6 +46,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Salary Structure",
+ "link_count": 0,
"link_to": "Salary Structure",
"link_type": "DocType",
"onboard": 1,
@@ -50,6 +57,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Salary Structure Assignment",
+ "link_count": 0,
"link_to": "Salary Structure Assignment",
"link_type": "DocType",
"onboard": 1,
@@ -60,6 +68,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Payroll Entry",
+ "link_count": 0,
"link_to": "Payroll Entry",
"link_type": "DocType",
"onboard": 1,
@@ -70,6 +79,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Salary Slip",
+ "link_count": 0,
"link_to": "Salary Slip",
"link_type": "DocType",
"onboard": 1,
@@ -79,6 +89,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Taxation",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -87,6 +98,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Payroll Period",
+ "link_count": 0,
"link_to": "Payroll Period",
"link_type": "DocType",
"onboard": 1,
@@ -97,6 +109,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Income Tax Slab",
+ "link_count": 0,
"link_to": "Income Tax Slab",
"link_type": "DocType",
"onboard": 1,
@@ -107,6 +120,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Other Income",
+ "link_count": 0,
"link_to": "Employee Other Income",
"link_type": "DocType",
"onboard": 1,
@@ -117,6 +131,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Tax Exemption Declaration",
+ "link_count": 0,
"link_to": "Employee Tax Exemption Declaration",
"link_type": "DocType",
"onboard": 1,
@@ -127,6 +142,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Tax Exemption Proof Submission",
+ "link_count": 0,
"link_to": "Employee Tax Exemption Proof Submission",
"link_type": "DocType",
"onboard": 1,
@@ -137,6 +153,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Tax Exemption Category",
+ "link_count": 0,
"link_to": "Employee Tax Exemption Category",
"link_type": "DocType",
"onboard": 0,
@@ -147,6 +164,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Tax Exemption Sub Category",
+ "link_count": 0,
"link_to": "Employee Tax Exemption Sub Category",
"link_type": "DocType",
"onboard": 0,
@@ -156,6 +174,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Compensations",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -164,6 +183,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Additional Salary",
+ "link_count": 0,
"link_to": "Additional Salary",
"link_type": "DocType",
"onboard": 1,
@@ -174,6 +194,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Retention Bonus",
+ "link_count": 0,
"link_to": "Retention Bonus",
"link_type": "DocType",
"onboard": 1,
@@ -184,6 +205,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Incentive",
+ "link_count": 0,
"link_to": "Employee Incentive",
"link_type": "DocType",
"onboard": 1,
@@ -194,6 +216,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Benefit Application",
+ "link_count": 0,
"link_to": "Employee Benefit Application",
"link_type": "DocType",
"onboard": 0,
@@ -204,6 +227,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Benefit Claim",
+ "link_count": 0,
"link_to": "Employee Benefit Claim",
"link_type": "DocType",
"onboard": 0,
@@ -213,6 +237,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -221,6 +246,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Salary Register",
+ "link_count": 0,
"link_to": "Salary Register",
"link_type": "Report",
"onboard": 0,
@@ -231,6 +257,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Salary Payments Based On Payment Mode",
+ "link_count": 0,
"link_to": "Salary Payments Based On Payment Mode",
"link_type": "Report",
"onboard": 0,
@@ -241,6 +268,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Salary Payments via ECS",
+ "link_count": 0,
"link_to": "Salary Payments via ECS",
"link_type": "Report",
"onboard": 0,
@@ -251,6 +279,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Income Tax Deductions",
+ "link_count": 0,
"link_to": "Income Tax Deductions",
"link_type": "Report",
"onboard": 0,
@@ -261,6 +290,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Professional Tax Deductions",
+ "link_count": 0,
"link_to": "Professional Tax Deductions",
"link_type": "Report",
"onboard": 0,
@@ -271,6 +301,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Provident Fund Deductions",
+ "link_count": 0,
"link_to": "Provident Fund Deductions",
"link_type": "Report",
"onboard": 0,
@@ -281,20 +312,26 @@
"hidden": 0,
"is_query_report": 1,
"label": "Bank Remittance",
+ "link_count": 0,
"link_to": "Bank Remittance",
"link_type": "Report",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2020-12-01 13:38:37.205628",
+ "modified": "2021-08-05 12:16:01.335324",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Payroll",
"onboarding": "Payroll",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 19,
"shortcuts": [
{
"label": "Salary Structure",
@@ -329,5 +366,6 @@
"link_to": "Payroll",
"type": "Dashboard"
}
- ]
+ ],
+ "title": "Payroll"
}
\ No newline at end of file
diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py
index 211b94a..d60b1a2 100644
--- a/erpnext/portal/product_configurator/utils.py
+++ b/erpnext/portal/product_configurator/utils.py
@@ -101,7 +101,7 @@
return html
def set_item_group_filters(field_filters):
- if 'item_group' in field_filters:
+ if field_filters is not None and 'item_group' in field_filters:
field_filters['item_group'] = [ig[0] for ig in get_child_groups(field_filters['item_group'])]
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index c8fbe0b..1e4b2b0 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -14,6 +14,7 @@
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
from frappe.model.document import Document
from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list
+from erpnext.controllers.employee_boarding_controller import update_employee_boarding_status
class Project(Document):
def get_feed(self):
@@ -37,6 +38,7 @@
self.send_welcome_email()
self.update_costing()
self.update_percent_complete()
+ update_employee_boarding_status(self)
def copy_from_template(self):
'''
@@ -132,6 +134,7 @@
def update_project(self):
'''Called externally by Task'''
self.update_percent_complete()
+ update_employee_boarding_status(self)
self.update_costing()
self.db_update()
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index c8bd80f..ae38d4c 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -15,12 +15,15 @@
WorkstationHolidayError)
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
from erpnext.setup.utils import get_exchange_rate
+from erpnext.hr.utils import validate_active_employee
class OverlapError(frappe.ValidationError): pass
class OverWorkLoggedError(frappe.ValidationError): pass
class Timesheet(Document):
def validate(self):
+ if self.employee:
+ validate_active_employee(self.employee)
self.set_employee_name()
self.set_status()
self.validate_dates()
diff --git a/erpnext/projects/workspace/projects/projects.json b/erpnext/projects/workspace/projects/projects.json
index c023a73..065f1ed 100644
--- a/erpnext/projects/workspace/projects/projects.json
+++ b/erpnext/projects/workspace/projects/projects.json
@@ -1,28 +1,32 @@
{
- "category": "Modules",
+ "category": "",
"charts": [
{
"chart_name": "Project Summary",
"label": "Open Projects"
}
],
+ "content": "[{\"type\": \"chart\", \"data\": {\"chart_name\": \"Open Projects\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Task\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Project\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Timesheet\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Project Billing Summary\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Projects\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Time Tracking\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
"creation": "2020-03-02 15:46:04.874669",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "project",
"idx": 0,
"is_default": 0,
- "is_standard": 1,
+ "is_standard": 0,
"label": "Projects",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Projects",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -31,6 +35,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Project",
+ "link_count": 0,
"link_to": "Project",
"link_type": "DocType",
"onboard": 1,
@@ -41,6 +46,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Task",
+ "link_count": 0,
"link_to": "Task",
"link_type": "DocType",
"onboard": 1,
@@ -51,6 +57,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Project Template",
+ "link_count": 0,
"link_to": "Project Template",
"link_type": "DocType",
"onboard": 0,
@@ -61,6 +68,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Project Type",
+ "link_count": 0,
"link_to": "Project Type",
"link_type": "DocType",
"onboard": 0,
@@ -71,6 +79,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Project Update",
+ "link_count": 0,
"link_to": "Project Update",
"link_type": "DocType",
"onboard": 0,
@@ -80,6 +89,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Time Tracking",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -88,6 +98,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Timesheet",
+ "link_count": 0,
"link_to": "Timesheet",
"link_type": "DocType",
"onboard": 1,
@@ -98,6 +109,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Activity Type",
+ "link_count": 0,
"link_to": "Activity Type",
"link_type": "DocType",
"onboard": 1,
@@ -108,6 +120,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Activity Cost",
+ "link_count": 0,
"link_to": "Activity Cost",
"link_type": "DocType",
"onboard": 0,
@@ -117,6 +130,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -125,6 +139,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Daily Timesheet Summary",
+ "link_count": 0,
"link_to": "Daily Timesheet Summary",
"link_type": "Report",
"onboard": 1,
@@ -135,6 +150,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Employee Hours Utilization",
+ "link_count": 0,
"link_to": "Employee Hours Utilization Based On Timesheet",
"link_type": "Report",
"onboard": 0,
@@ -145,6 +161,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Project Profitability",
+ "link_count": 0,
"link_to": "Project Profitability",
"link_type": "Report",
"onboard": 0,
@@ -155,6 +172,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Project wise Stock Tracking",
+ "link_count": 0,
"link_to": "Project wise Stock Tracking",
"link_type": "Report",
"onboard": 0,
@@ -165,6 +183,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Project Billing Summary",
+ "link_count": 0,
"link_to": "Project Billing Summary",
"link_type": "Report",
"onboard": 0,
@@ -175,19 +194,26 @@
"hidden": 0,
"is_query_report": 1,
"label": "Delayed Tasks Summary",
+ "link_count": 0,
"link_to": "Delayed Tasks Summary",
"link_type": "Report",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2021-04-25 16:27:16.548780",
+ "modified": "2021-08-05 12:16:01.540145",
"modified_by": "Administrator",
"module": "Projects",
"name": "Projects",
+ "onboarding": "",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 20,
"shortcuts": [
{
"color": "Blue",
@@ -220,5 +246,6 @@
"link_to": "Project",
"type": "Dashboard"
}
- ]
+ ],
+ "title": "Projects"
}
\ No newline at end of file
diff --git a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js
index 142fe79..239fbb9 100644
--- a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js
+++ b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js
@@ -16,7 +16,7 @@
doctype: "Bank Transaction",
filters: { name: this.bank_transaction_name },
fieldname: [
- "date",
+ "date as reference_date",
"deposit",
"withdrawal",
"currency",
diff --git a/erpnext/public/js/contact.js b/erpnext/public/js/contact.js
new file mode 100644
index 0000000..41a0e8a
--- /dev/null
+++ b/erpnext/public/js/contact.js
@@ -0,0 +1,16 @@
+
+
+frappe.ui.form.on("Contact", {
+ refresh(frm) {
+ frm.set_query('link_doctype', "links", function() {
+ return {
+ query: "frappe.contacts.address_and_contact.filter_dynamic_link_doctypes",
+ filters: {
+ fieldtype: ["in", ["HTML", "Text Editor"]],
+ fieldname: ["in", ["contact_html", "company_description"]],
+ }
+ };
+ });
+ frm.refresh_field("links");
+ }
+});
diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js
index 7b997a1..84c7176 100644
--- a/erpnext/public/js/controllers/accounts.js
+++ b/erpnext/public/js/controllers/accounts.js
@@ -31,6 +31,14 @@
}
}
});
+ frm.set_query("cost_center", "taxes", function(doc) {
+ return {
+ filters: {
+ "company": doc.company,
+ "is_group": 0
+ }
+ };
+ });
}
},
validate: function(frm) {
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 181e340..84697e0 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -65,7 +65,7 @@
this.frm.refresh_fields();
}
- calculate_discount_amount(){
+ calculate_discount_amount() {
if (frappe.meta.get_docfield(this.frm.doc.doctype, "discount_amount")) {
this.calculate_item_values();
this.calculate_net_total();
@@ -75,18 +75,15 @@
}
_calculate_taxes_and_totals() {
- frappe.run_serially([
- () => this.validate_conversion_rate(),
- () => this.calculate_item_values(),
- () => this.update_item_tax_map(),
- () => this.initialize_taxes(),
- () => this.determine_exclusive_rate(),
- () => this.calculate_net_total(),
- () => this.calculate_taxes(),
- () => this.manipulate_grand_total_for_inclusive_tax(),
- () => this.calculate_totals(),
- () => this._cleanup()
- ]);
+ this.validate_conversion_rate();
+ this.calculate_item_values();
+ this.initialize_taxes();
+ this.determine_exclusive_rate();
+ this.calculate_net_total();
+ this.calculate_taxes();
+ this.manipulate_grand_total_for_inclusive_tax();
+ this.calculate_totals();
+ this._cleanup();
}
validate_conversion_rate() {
@@ -270,46 +267,6 @@
frappe.model.round_floats_in(this.frm.doc, ["total", "base_total", "net_total", "base_net_total"]);
}
- update_item_tax_map() {
- let me = this;
- let item_codes = [];
- let item_rates = {};
- let item_tax_templates = {};
-
- $.each(this.frm.doc.items || [], function(i, item) {
- if (item.item_code) {
- // Use combination of name and item code in case same item is added multiple times
- item_codes.push([item.item_code, item.name]);
- item_rates[item.name] = item.net_rate;
- item_tax_templates[item.name] = item.item_tax_template;
- }
- });
-
- if (item_codes.length) {
- return this.frm.call({
- method: "erpnext.stock.get_item_details.get_item_tax_info",
- args: {
- company: me.frm.doc.company,
- tax_category: cstr(me.frm.doc.tax_category),
- item_codes: item_codes,
- item_rates: item_rates,
- item_tax_templates: item_tax_templates
- },
- callback: function(r) {
- if (!r.exc) {
- $.each(me.frm.doc.items || [], function(i, item) {
- if (item.name && r.message.hasOwnProperty(item.name) && r.message[item.name].item_tax_template) {
- item.item_tax_template = r.message[item.name].item_tax_template;
- item.item_tax_rate = r.message[item.name].item_tax_rate;
- me.add_taxes_from_item_tax_template(item.item_tax_rate);
- }
- });
- }
- }
- });
- }
- }
-
add_taxes_from_item_tax_template(item_tax_map) {
let me = this;
@@ -634,8 +591,6 @@
tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail);
});
}
-
- this.frm.refresh_fields();
}
set_discount_amount() {
@@ -796,8 +751,6 @@
this.frm.doc.payments.find(pay => {
if (pay.default) {
pay.amount = total_amount_to_pay;
- } else {
- pay.amount = 0.0
}
});
this.frm.refresh_fields();
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 8360337..3c6c347 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -752,7 +752,7 @@
this.frm.trigger("item_code", cdt, cdn);
}
else {
- // Replacing all occurences of comma with carriage return
+ // Replace all occurences of comma with line feed
item.serial_no = item.serial_no.replace(/,/g, '\n');
item.conversion_factor = item.conversion_factor || 1;
refresh_field("serial_no", item.name, item.parentfield);
@@ -846,9 +846,9 @@
frappe.run_serially([
() => me.frm.script_manager.trigger("currency"),
+ () => me.update_item_tax_map(),
() => me.apply_default_taxes(),
- () => me.apply_pricing_rule(),
- () => me.calculate_taxes_and_totals()
+ () => me.apply_pricing_rule()
]);
}
}
@@ -1807,6 +1807,46 @@
]);
}
+ update_item_tax_map() {
+ let me = this;
+ let item_codes = [];
+ let item_rates = {};
+ let item_tax_templates = {};
+
+ $.each(this.frm.doc.items || [], function(i, item) {
+ if (item.item_code) {
+ // Use combination of name and item code in case same item is added multiple times
+ item_codes.push([item.item_code, item.name]);
+ item_rates[item.name] = item.net_rate;
+ item_tax_templates[item.name] = item.item_tax_template;
+ }
+ });
+
+ if (item_codes.length) {
+ return this.frm.call({
+ method: "erpnext.stock.get_item_details.get_item_tax_info",
+ args: {
+ company: me.frm.doc.company,
+ tax_category: cstr(me.frm.doc.tax_category),
+ item_codes: item_codes,
+ item_rates: item_rates,
+ item_tax_templates: item_tax_templates
+ },
+ callback: function(r) {
+ if (!r.exc) {
+ $.each(me.frm.doc.items || [], function(i, item) {
+ if (item.name && r.message.hasOwnProperty(item.name) && r.message[item.name].item_tax_template) {
+ item.item_tax_template = r.message[item.name].item_tax_template;
+ item.item_tax_rate = r.message[item.name].item_tax_rate;
+ me.add_taxes_from_item_tax_template(item.item_tax_rate);
+ }
+ });
+ }
+ }
+ });
+ }
+ }
+
item_tax_template(doc, cdt, cdn) {
var me = this;
if(me.frm.updating_party_details) return;
diff --git a/erpnext/public/js/utils/customer_quick_entry.js b/erpnext/public/js/utils/customer_quick_entry.js
index efb8dd9..d2c5c72 100644
--- a/erpnext/public/js/utils/customer_quick_entry.js
+++ b/erpnext/public/js/utils/customer_quick_entry.js
@@ -1,8 +1,8 @@
frappe.provide('frappe.ui.form');
frappe.ui.form.CustomerQuickEntryForm = class CustomerQuickEntryForm extends frappe.ui.form.QuickEntryForm {
- constructor(doctype, after_insert) {
- super(doctype, after_insert);
+ constructor(doctype, after_insert, init_callback, doc, force) {
+ super(doctype, after_insert, init_callback, doc, force);
this.skip_redirect_on_error = true;
}
diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js
index a79eadc..4d432e3 100644
--- a/erpnext/public/js/utils/party.js
+++ b/erpnext/public/js/utils/party.js
@@ -76,6 +76,7 @@
if (args) {
args.posting_date = frm.doc.posting_date || frm.doc.transaction_date;
+ args.fetch_payment_terms_template = cint(!frm.doc.ignore_default_payment_terms_template);
}
}
if (!args || !args.party) return;
diff --git a/erpnext/quality_management/workspace/quality/quality.json b/erpnext/quality_management/workspace/quality/quality.json
index e5fef43..4dc8129 100644
--- a/erpnext/quality_management/workspace/quality/quality.json
+++ b/erpnext/quality_management/workspace/quality/quality.json
@@ -1,22 +1,27 @@
{
- "category": "Modules",
+ "category": "",
"charts": [],
+ "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Goal\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Procedure\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Inspection\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Review\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Action\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Non Conformance\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Goal and Procedure\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Feedback\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Meeting\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Review and Action\", \"col\": 4}}]",
"creation": "2020-03-02 15:49:28.632014",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "quality",
"idx": 0,
- "is_standard": 1,
+ "is_default": 0,
+ "is_standard": 0,
"label": "Quality",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Goal and Procedure",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -25,6 +30,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Quality Goal",
+ "link_count": 0,
"link_to": "Quality Goal",
"link_type": "DocType",
"onboard": 1,
@@ -35,6 +41,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Quality Procedure",
+ "link_count": 0,
"link_to": "Quality Procedure",
"link_type": "DocType",
"onboard": 1,
@@ -45,6 +52,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Tree of Procedures",
+ "link_count": 0,
"link_to": "Quality Procedure",
"link_type": "DocType",
"onboard": 0,
@@ -54,6 +62,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Feedback",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -62,6 +71,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Quality Feedback",
+ "link_count": 0,
"link_to": "Quality Feedback",
"link_type": "DocType",
"onboard": 1,
@@ -72,6 +82,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Quality Feedback Template",
+ "link_count": 0,
"link_to": "Quality Feedback Template",
"link_type": "DocType",
"onboard": 0,
@@ -81,6 +92,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Meeting",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -89,6 +101,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Quality Meeting",
+ "link_count": 0,
"link_to": "Quality Meeting",
"link_type": "DocType",
"onboard": 0,
@@ -98,6 +111,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Review and Action",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -106,6 +120,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Non Conformance",
+ "link_count": 0,
"link_to": "Non Conformance",
"link_type": "DocType",
"onboard": 0,
@@ -116,6 +131,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Quality Review",
+ "link_count": 0,
"link_to": "Quality Review",
"link_type": "DocType",
"onboard": 0,
@@ -126,19 +142,26 @@
"hidden": 0,
"is_query_report": 0,
"label": "Quality Action",
+ "link_count": 0,
"link_to": "Quality Action",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2020-12-01 13:38:35.120213",
+ "modified": "2021-08-05 12:16:01.699912",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality",
+ "onboarding": "",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 21,
"shortcuts": [
{
"color": "Grey",
@@ -186,5 +209,6 @@
"stats_filter": "{\"status\": \"Open\"}",
"type": "DocType"
}
- ]
+ ],
+ "title": "Quality"
}
\ No newline at end of file
diff --git a/erpnext/regional/address_template/templates/france.html b/erpnext/regional/address_template/templates/france.html
new file mode 100644
index 0000000..752331e
--- /dev/null
+++ b/erpnext/regional/address_template/templates/france.html
@@ -0,0 +1,5 @@
+{% if address_line1 %}{{ address_line1 }}{% endif -%}
+{% if address_line2 %}<br>{{ address_line2 }}{% endif -%}
+{% if pincode %}<br>{{ pincode }}{% endif -%}
+{% if city %} {{ city }}{% endif -%}
+{% if country %}<br>{{ country }}{% endif -%}
diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js
index cc2d9f0..54e4886 100644
--- a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js
+++ b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js
@@ -3,7 +3,7 @@
frappe.ui.form.on('E Invoice Settings', {
refresh(frm) {
- const docs_link = 'https://docs.erpnext.com/docs/user/manual/en/regional/india/setup-e-invoicing';
+ const docs_link = 'https://docs.erpnext.com/docs/v13/user/manual/en/regional/india/setup-e-invoicing';
frm.dashboard.set_headline(
__("Read {0} for more information on E Invoicing features.", [`<a href='${docs_link}'>documentation</a>`])
);
diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
index 6415204..0ee5b09 100644
--- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
+++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
@@ -214,9 +214,8 @@
for d in item_details:
if d.item_code not in self.invoice_items.get(d.parent, {}):
- self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code,
- sum((i.get('taxable_value', 0) or i.get('base_net_amount', 0)) for i in item_details
- if i.item_code == d.item_code and i.parent == d.parent))
+ self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, 0.0)
+ self.invoice_items[d.parent][d.item_code] += d.get('taxable_value', 0) or d.get('base_net_amount', 0)
if d.is_nil_exempt and d.item_code not in self.is_nil_exempt:
self.is_nil_exempt.append(d.item_code)
@@ -281,9 +280,15 @@
if self.get('invoice_items'):
# Build itemised tax for export invoices, nil and exempted where tax table is blank
for invoice, items in iteritems(self.invoice_items):
- if invoice not in self.items_based_on_tax_rate and (self.invoice_detail_map.get(invoice, {}).get('export_type')
- == "Without Payment of Tax"):
+ if invoice not in self.items_based_on_tax_rate and self.invoice_detail_map.get(invoice, {}).get('export_type') \
+ == "Without Payment of Tax" and self.invoice_detail_map.get(invoice, {}).get('gst_category') == "Overseas":
self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())
+ else:
+ for item in items.keys():
+ if item in self.is_nil_exempt + self.is_non_gst and \
+ item not in self.items_based_on_tax_rate.get(invoice, {}).get(0, []):
+ self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, [])
+ self.items_based_on_tax_rate[invoice][0].append(item)
def set_outward_taxable_supplies(self):
inter_state_supply_details = {}
@@ -322,6 +327,9 @@
inter_state_supply_details[(gst_category, place_of_supply)]['txval'] += taxable_value
inter_state_supply_details[(gst_category, place_of_supply)]['iamt'] += (taxable_value * rate /100)
+ if self.invoice_cess.get(inv):
+ self.report_dict['sup_details']['osup_det']['csamt'] += flt(self.invoice_cess.get(inv), 2)
+
self.set_inter_state_supply(inter_state_supply_details)
def set_supplies_liable_to_reverse_charge(self):
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/__init__.py b/erpnext/regional/doctype/south_africa_vat_settings/__init__.py
similarity index 100%
rename from erpnext/erpnext_integrations/doctype/shopify_settings/__init__.py
rename to erpnext/regional/doctype/south_africa_vat_settings/__init__.py
diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js
new file mode 100644
index 0000000..e37a61a
--- /dev/null
+++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js
@@ -0,0 +1,23 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('South Africa VAT Settings', {
+ refresh: function(frm) {
+ frm.set_query("company", function() {
+ return {
+ filters: {
+ country: "South Africa",
+ }
+ };
+ });
+ frm.set_query("account", "vat_accounts", function() {
+ return {
+ filters: {
+ company: frm.doc.company,
+ account_type: "Tax",
+ is_group: 0
+ }
+ };
+ });
+ }
+});
diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.json b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.json
new file mode 100644
index 0000000..8a51829
--- /dev/null
+++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.json
@@ -0,0 +1,76 @@
+{
+ "actions": [],
+ "autoname": "field:company",
+ "creation": "2021-07-08 22:34:33.668015",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "company",
+ "vat_accounts"
+ ],
+ "fields": [
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Company",
+ "options": "Company",
+ "reqd": 1,
+ "unique": 1
+ },
+ {
+ "fieldname": "vat_accounts",
+ "fieldtype": "Table",
+ "label": "VAT Accounts",
+ "options": "South Africa VAT Account",
+ "reqd": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-07-14 02:17:52.476762",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "South Africa VAT Settings",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts Manager",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Auditor",
+ "share": 1
+ }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py
new file mode 100644
index 0000000..d74154b
--- /dev/null
+++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+class SouthAfricaVATSettings(Document):
+ pass
diff --git a/erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py b/erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py
new file mode 100644
index 0000000..1c36652
--- /dev/null
+++ b/erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+class TestSouthAfricaVATSettings(unittest.TestCase):
+ pass
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index 81c7a6b..fa7e88d 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -190,8 +190,10 @@
item.description = sanitize_for_json(d.item_name)
item.qty = abs(item.qty)
-
- item.unit_rate = abs(item.taxable_value / item.qty)
+ if flt(item.qty) != 0.0:
+ item.unit_rate = abs(item.taxable_value / item.qty)
+ else:
+ item.unit_rate = abs(item.taxable_value)
item.gross_amount = abs(item.taxable_value)
item.taxable_value = abs(item.taxable_value)
item.discount_amount = 0
@@ -316,10 +318,6 @@
))
def get_return_doc_reference(invoice):
- if not invoice.return_against:
- frappe.throw(_('For generating IRN, reference to the original invoice is mandatory for a credit note. Please set {} field to generate e-invoice.')
- .format(frappe.bold('Return Against')), title=_('Missing Field'))
-
invoice_date = frappe.db.get_value('Sales Invoice', invoice.return_against, 'posting_date')
return frappe._dict(dict(
invoice_name=invoice.return_against, invoice_date=format_date(invoice_date, 'dd/mm/yyyy')
@@ -438,7 +436,7 @@
if invoice.is_pos and invoice.base_paid_amount:
payment_details = get_payment_details(invoice)
- if invoice.is_return:
+ if invoice.is_return and invoice.return_against:
prev_doc_details = get_return_doc_reference(invoice)
if invoice.transporter and not invoice.is_return:
@@ -932,7 +930,7 @@
def set_einvoice_data(self, res):
enc_signed_invoice = res.get('SignedInvoice')
- dec_signed_invoice = jwt.decode(enc_signed_invoice, verify=False)['data']
+ dec_signed_invoice = jwt.decode(enc_signed_invoice, options={"verify_signature": False})['data']
self.invoice.irn = res.get('Irn')
self.invoice.ewaybill = res.get('EwbNo')
@@ -969,7 +967,7 @@
"attached_to_doctype": doctype,
"attached_to_name": docname,
"attached_to_field": "qrcode_image",
- "is_private": 1,
+ "is_private": 0,
"content": qr_image.getvalue()})
_file.save()
frappe.db.commit()
@@ -1130,4 +1128,4 @@
def job_already_enqueued(job_name):
enqueued_jobs = [d.get("job_name") for d in get_info()]
if job_name in enqueued_jobs:
- return True
\ No newline at end of file
+ return True
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 9265460..b4f146c 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -457,7 +457,7 @@
depends_on='eval:in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category) && doc.irn_cancelled === 0'),
dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
- depends_on='eval:(doc.irn_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),
+ depends_on='eval: doc.irn', allow_on_submit=1, insert_after='customer'),
dict(fieldname='eway_bill_validity', label='E-Way Bill Validity', fieldtype='Data', no_copy=1, print_hide=1,
depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill'),
@@ -641,7 +641,6 @@
'label': 'Export Type',
'fieldtype': 'Select',
'insert_after': 'gst_category',
- 'default': 'Without Payment of Tax',
'depends_on':'eval:in_list(["SEZ", "Overseas"], doc.gst_category)',
'options': '\nWith Payment of Tax\nWithout Payment of Tax'
}
@@ -660,7 +659,6 @@
'label': 'Export Type',
'fieldtype': 'Select',
'insert_after': 'gst_category',
- 'default': 'Without Payment of Tax',
'depends_on':'eval:in_list(["SEZ", "Overseas", "Deemed Export"], doc.gst_category)',
'options': '\nWith Payment of Tax\nWithout Payment of Tax'
}
@@ -987,4 +985,4 @@
def update_accounts_settings_for_taxes():
if frappe.db.count('Company') == 1:
- frappe.db.set_value('Accounts Settings', None, "add_taxes_from_item_tax_template", 0)
\ No newline at end of file
+ frappe.db.set_value('Accounts Settings', None, "add_taxes_from_item_tax_template", 0)
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 81c0918..a152797 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -431,9 +431,11 @@
company_address = frappe.get_doc('Address', doc.company_address)
billing_address = frappe.get_doc('Address', doc.customer_address)
+ #added dispatch address
+ dispatch_address = frappe.get_doc('Address', doc.dispatch_address_name) if doc.dispatch_address_name else company_address
shipping_address = frappe.get_doc('Address', doc.shipping_address_name)
- data = get_address_details(data, doc, company_address, billing_address)
+ data = get_address_details(data, doc, company_address, billing_address, dispatch_address)
data.itemList = []
data.totalValue = doc.total
@@ -519,10 +521,10 @@
`tabDynamic Link`.link_name = %(company)s""", {"company": company})
return company_gstins
-def get_address_details(data, doc, company_address, billing_address):
+def get_address_details(data, doc, company_address, billing_address, dispatch_address):
data.fromPincode = validate_pincode(company_address.pincode, 'Company Address')
- data.fromStateCode = data.actualFromStateCode = validate_state_code(
- company_address.gst_state_number, 'Company Address')
+ data.fromStateCode = validate_state_code(company_address.gst_state_number, 'Company Address')
+ data.actualFromStateCode = validate_state_code(dispatch_address.gst_state_number, 'Dispatch Address')
if not doc.billing_address_gstin or len(doc.billing_address_gstin) < 15:
data.toGstin = 'URP'
@@ -849,7 +851,7 @@
# if its the first depreciation
if depreciable_value == asset.gross_purchase_amount:
# as per IT act, if the asset is purchased in the 2nd half of fiscal year, then rate is divided by 2
- diff = date_diff(asset.available_for_use_date, row.depreciation_start_date)
+ diff = date_diff(row.depreciation_start_date, asset.available_for_use_date)
if diff <= 180:
rate_of_depreciation = rate_of_depreciation / 2
frappe.msgprint(
@@ -857,4 +859,15 @@
depreciation_amount = flt(depreciable_value * (flt(rate_of_depreciation) / 100))
- return depreciation_amount
\ No newline at end of file
+ return depreciation_amount
+
+def set_item_tax_from_hsn_code(item):
+ if not item.taxes and item.gst_hsn_code:
+ hsn_doc = frappe.get_doc("GST HSN Code", item.gst_hsn_code)
+
+ for tax in hsn_doc.taxes:
+ item.append('taxes', {
+ 'item_tax_template': tax.item_tax_template,
+ 'tax_category': tax.tax_category,
+ 'valid_from': tax.valid_from
+ })
\ No newline at end of file
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index cfcb8c3..4b73094 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -217,9 +217,8 @@
for d in items:
if d.item_code not in self.invoice_items.get(d.parent, {}):
- self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code,
- sum((i.get('taxable_value', 0) or i.get('base_net_amount', 0)) for i in items
- if i.item_code == d.item_code and i.parent == d.parent))
+ self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, 0.0)
+ self.invoice_items[d.parent][d.item_code] += d.get('taxable_value', 0) or d.get('base_net_amount', 0)
item_tax_rate = {}
@@ -287,7 +286,8 @@
# Build itemised tax for export invoices where tax table is blank
for invoice, items in iteritems(self.invoice_items):
if invoice not in self.items_based_on_tax_rate and invoice not in unidentified_gst_accounts_invoice \
- and frappe.db.get_value(self.doctype, invoice, "export_type") == "Without Payment of Tax":
+ and self.invoices.get(invoice, {}).get('export_type') == "Without Payment of Tax" \
+ and self.invoices.get(invoice, {}).get('gst_category') == "Overseas":
self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())
def get_columns(self):
diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/__init__.py b/erpnext/regional/report/vat_audit_report/__init__.py
similarity index 100%
copy from erpnext/erpnext_integrations/doctype/shopify_log/__init__.py
copy to erpnext/regional/report/vat_audit_report/__init__.py
diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.js b/erpnext/regional/report/vat_audit_report/vat_audit_report.js
new file mode 100644
index 0000000..39ef9b5
--- /dev/null
+++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.js
@@ -0,0 +1,31 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["VAT Audit Report"] = {
+ "filters": [
+ {
+ "fieldname": "company",
+ "label": __("Company"),
+ "fieldtype": "Link",
+ "options": "Company",
+ "reqd": 1,
+ "default": frappe.defaults.get_user_default("Company")
+ },
+ {
+ "fieldname": "from_date",
+ "label": __("From Date"),
+ "fieldtype": "Date",
+ "reqd": 1,
+ "default": frappe.datetime.add_months(frappe.datetime.get_today(), -2),
+ "width": "80"
+ },
+ {
+ "fieldname": "to_date",
+ "label": __("To Date"),
+ "fieldtype": "Date",
+ "reqd": 1,
+ "default": frappe.datetime.get_today()
+ }
+ ]
+};
diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.json b/erpnext/regional/report/vat_audit_report/vat_audit_report.json
new file mode 100644
index 0000000..a8be7bf
--- /dev/null
+++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.json
@@ -0,0 +1,22 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-07-09 11:07:43.473518",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-07-09 11:07:43.473518",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "VAT Audit Report",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "GL Entry",
+ "report_name": "VAT Audit Report",
+ "report_type": "Script Report",
+ "roles": []
+}
\ No newline at end of file
diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.py b/erpnext/regional/report/vat_audit_report/vat_audit_report.py
new file mode 100644
index 0000000..292605e
--- /dev/null
+++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py
@@ -0,0 +1,269 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+import json
+from frappe import _
+from frappe.utils import formatdate
+
+def execute(filters=None):
+ return VATAuditReport(filters).run()
+
+class VATAuditReport(object):
+
+ def __init__(self, filters=None):
+ self.filters = frappe._dict(filters or {})
+ self.columns = []
+ self.data = []
+ self.doctypes = ["Purchase Invoice", "Sales Invoice"]
+
+ def run(self):
+ self.get_sa_vat_accounts()
+ self.get_columns()
+ for doctype in self.doctypes:
+ self.select_columns = """
+ name as voucher_no,
+ posting_date, remarks"""
+ columns = ", supplier as party, credit_to as account" if doctype=="Purchase Invoice" \
+ else ", customer as party, debit_to as account"
+ self.select_columns += columns
+
+ self.get_invoice_data(doctype)
+
+ if self.invoices:
+ self.get_invoice_items(doctype)
+ self.get_items_based_on_tax_rate(doctype)
+ self.get_data(doctype)
+
+ return self.columns, self.data
+
+ def get_sa_vat_accounts(self):
+ self.sa_vat_accounts = frappe.get_list("South Africa VAT Account",
+ filters = {"parent": self.filters.company}, pluck="account")
+ if not self.sa_vat_accounts and not frappe.flags.in_test and not frappe.flags.in_migrate:
+ frappe.throw(_("Please set VAT Accounts in South Africa VAT Settings"))
+
+ def get_invoice_data(self, doctype):
+ conditions = self.get_conditions()
+ self.invoices = frappe._dict()
+
+ invoice_data = frappe.db.sql("""
+ SELECT
+ {select_columns}
+ FROM
+ `tab{doctype}`
+ WHERE
+ docstatus = 1 {where_conditions}
+ and is_opening = "No"
+ ORDER BY
+ posting_date DESC
+ """.format(select_columns=self.select_columns, doctype=doctype,
+ where_conditions=conditions), self.filters, as_dict=1)
+
+ for d in invoice_data:
+ self.invoices.setdefault(d.voucher_no, d)
+
+ def get_invoice_items(self, doctype):
+ self.invoice_items = frappe._dict()
+
+ items = frappe.db.sql("""
+ SELECT
+ item_code, parent, taxable_value, base_net_amount, is_zero_rated
+ FROM
+ `tab%s Item`
+ WHERE
+ parent in (%s)
+ """ % (doctype, ", ".join(["%s"]*len(self.invoices))), tuple(self.invoices), as_dict=1)
+ for d in items:
+ if d.item_code not in self.invoice_items.get(d.parent, {}):
+ self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, {
+ 'net_amount': 0.0})
+ self.invoice_items[d.parent][d.item_code]['net_amount'] += d.get('taxable_value', 0) or d.get('base_net_amount', 0)
+ self.invoice_items[d.parent][d.item_code]['is_zero_rated'] = d.is_zero_rated
+
+ def get_items_based_on_tax_rate(self, doctype):
+ self.items_based_on_tax_rate = frappe._dict()
+ self.item_tax_rate = frappe._dict()
+ self.tax_doctype = "Purchase Taxes and Charges" if doctype=="Purchase Invoice" \
+ else "Sales Taxes and Charges"
+
+ self.tax_details = frappe.db.sql("""
+ SELECT
+ parent, account_head, item_wise_tax_detail, base_tax_amount_after_discount_amount
+ FROM
+ `tab%s`
+ WHERE
+ parenttype = %s and docstatus = 1
+ and parent in (%s)
+ ORDER BY
+ account_head
+ """ % (self.tax_doctype, "%s", ", ".join(["%s"]*len(self.invoices.keys()))),
+ tuple([doctype] + list(self.invoices.keys())))
+
+ for parent, account, item_wise_tax_detail, tax_amount in self.tax_details:
+ if item_wise_tax_detail:
+ try:
+ if account in self.sa_vat_accounts:
+ item_wise_tax_detail = json.loads(item_wise_tax_detail)
+ else:
+ continue
+ for item_code, taxes in item_wise_tax_detail.items():
+ is_zero_rated = self.invoice_items.get(parent).get(item_code).get("is_zero_rated")
+ #to skip items with non-zero tax rate in multiple rows
+ if taxes[0] == 0 and not is_zero_rated:
+ continue
+ tax_rate, item_amount_map = self.get_item_amount_map(parent, item_code, taxes)
+
+ if tax_rate is not None:
+ rate_based_dict = self.items_based_on_tax_rate.setdefault(parent, {}) \
+ .setdefault(tax_rate, [])
+ if item_code not in rate_based_dict:
+ rate_based_dict.append(item_code)
+ except ValueError:
+ continue
+
+ def get_item_amount_map(self, parent, item_code, taxes):
+ net_amount = self.invoice_items.get(parent).get(item_code).get("net_amount")
+ tax_rate = taxes[0]
+ tax_amount = taxes[1]
+ gross_amount = net_amount + tax_amount
+ item_amount_map = self.item_tax_rate.setdefault(parent, {}) \
+ .setdefault(item_code, [])
+ amount_dict = {
+ "tax_rate": tax_rate,
+ "gross_amount": gross_amount,
+ "tax_amount": tax_amount,
+ "net_amount": net_amount
+ }
+ item_amount_map.append(amount_dict)
+
+ return tax_rate, item_amount_map
+
+ def get_conditions(self):
+ conditions = ""
+ for opts in (("company", " and company=%(company)s"),
+ ("from_date", " and posting_date>=%(from_date)s"),
+ ("to_date", " and posting_date<=%(to_date)s")):
+ if self.filters.get(opts[0]):
+ conditions += opts[1]
+
+ return conditions
+
+ def get_data(self, doctype):
+ consolidated_data = self.get_consolidated_data(doctype)
+ section_name = _("Purchases") if doctype == "Purchase Invoice" else _("Sales")
+
+ for rate, section in consolidated_data.items():
+ rate = int(rate)
+ label = frappe.bold(section_name + "- " + "Rate" + " " + str(rate) + "%")
+ section_head = {"posting_date": label}
+ total_gross = total_tax = total_net = 0
+ self.data.append(section_head)
+ for row in section.get("data"):
+ self.data.append(row)
+ total_gross += row["gross_amount"]
+ total_tax += row["tax_amount"]
+ total_net += row["net_amount"]
+
+ total = {
+ "posting_date": frappe.bold(_("Total")),
+ "gross_amount": total_gross,
+ "tax_amount": total_tax,
+ "net_amount": total_net,
+ "bold":1
+ }
+ self.data.append(total)
+ self.data.append({})
+
+ def get_consolidated_data(self, doctype):
+ consolidated_data_map={}
+ for inv, inv_data in self.invoices.items():
+ if self.items_based_on_tax_rate.get(inv):
+ for rate, items in self.items_based_on_tax_rate.get(inv).items():
+ consolidated_data_map.setdefault(rate, {"data": []})
+ for item in items:
+ row = {}
+ item_details = self.item_tax_rate.get(inv).get(item)
+ row["account"] = inv_data.get("account")
+ row["posting_date"] = formatdate(inv_data.get("posting_date"), "dd-mm-yyyy")
+ row["voucher_type"] = doctype
+ row["voucher_no"] = inv
+ row["party_type"] = "Customer" if doctype == "Sales Invoice" else "Supplier"
+ row["party"] = inv_data.get("party")
+ row["remarks"] = inv_data.get("remarks")
+ row["gross_amount"]= item_details[0].get("gross_amount")
+ row["tax_amount"]= item_details[0].get("tax_amount")
+ row["net_amount"]= item_details[0].get("net_amount")
+ consolidated_data_map[rate]["data"].append(row)
+
+ return consolidated_data_map
+
+ def get_columns(self):
+ self.columns = [
+ {
+ "fieldname": "posting_date",
+ "label": "Posting Date",
+ "fieldtype": "Data",
+ "width": 200
+ },
+ {
+ "fieldname": "account",
+ "label": "Account",
+ "fieldtype": "Link",
+ "options": "Account",
+ "width": 150
+ },
+ {
+ "fieldname": "voucher_type",
+ "label": "Voucher Type",
+ "fieldtype": "Data",
+ "width": 140,
+ "hidden": 1
+ },
+ {
+ "fieldname": "voucher_no",
+ "label": "Reference",
+ "fieldtype": "Dynamic Link",
+ "options": "voucher_type",
+ "width": 150
+ },
+ {
+ "fieldname": "party_type",
+ "label": "Party Type",
+ "fieldtype": "Data",
+ "width": 140,
+ "hidden": 1
+ },
+ {
+ "fieldname": "party",
+ "label": "Party",
+ "fieldtype": "Dynamic Link",
+ "options": "party_type",
+ "width": 150
+ },
+ {
+ "fieldname": "remarks",
+ "label": "Details",
+ "fieldtype": "Data",
+ "width": 150
+ },
+ {
+ "fieldname": "net_amount",
+ "label": "Net Amount",
+ "fieldtype": "Currency",
+ "width": 130
+ },
+ {
+ "fieldname": "tax_amount",
+ "label": "Tax Amount",
+ "fieldtype": "Currency",
+ "width": 130
+ },
+ {
+ "fieldname": "gross_amount",
+ "label": "Gross Amount",
+ "fieldtype": "Currency",
+ "width": 130
+ },
+ ]
diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/__init__.py b/erpnext/regional/south_africa/__init__.py
similarity index 100%
copy from erpnext/erpnext_integrations/doctype/shopify_log/__init__.py
copy to erpnext/regional/south_africa/__init__.py
diff --git a/erpnext/regional/south_africa/setup.py b/erpnext/regional/south_africa/setup.py
new file mode 100644
index 0000000..4657ff8
--- /dev/null
+++ b/erpnext/regional/south_africa/setup.py
@@ -0,0 +1,50 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+from frappe.permissions import add_permission, update_permission_property
+
+def setup(company=None, patch=True):
+ make_custom_fields()
+ add_permissions()
+
+def make_custom_fields(update=True):
+ is_zero_rated = dict(fieldname='is_zero_rated', label='Is Zero Rated',
+ fieldtype='Check', fetch_from='item_code.is_zero_rated',
+ insert_after='description', print_hide=1)
+ custom_fields = {
+ 'Item': [
+ dict(fieldname='is_zero_rated', label='Is Zero Rated',
+ fieldtype='Check', insert_after='item_group',
+ print_hide=1)
+ ],
+ 'Sales Invoice Item': is_zero_rated,
+ 'Purchase Invoice Item': is_zero_rated
+ }
+
+ create_custom_fields(custom_fields, update=update)
+
+def add_permissions():
+ """Add Permissions for South Africa VAT Settings and South Africa VAT Account
+ and VAT Audit Report"""
+ for doctype in ('South Africa VAT Settings', 'South Africa VAT Account'):
+ add_permission(doctype, 'All', 0)
+ for role in ('Accounts Manager', 'Accounts User', 'System Manager'):
+ add_permission(doctype, role, 0)
+ update_permission_property(doctype, role, 0, 'write', 1)
+ update_permission_property(doctype, role, 0, 'create', 1)
+
+
+ if not frappe.db.get_value('Custom Role', dict(report="VAT Audit Report")):
+ frappe.get_doc(dict(
+ doctype='Custom Role',
+ report="VAT Audit Report",
+ roles= [
+ dict(role='Accounts User'),
+ dict(role='Accounts Manager'),
+ dict(role='Auditor')
+ ]
+ )).insert()
\ No newline at end of file
diff --git a/erpnext/selling/doctype/campaign/README.md b/erpnext/selling/doctype/campaign/README.md
deleted file mode 100644
index a837318..0000000
--- a/erpnext/selling/doctype/campaign/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Sales campaign / promotion, like special discount, exhibition, newsletter etc.
\ No newline at end of file
diff --git a/erpnext/selling/doctype/campaign/__init__.py b/erpnext/selling/doctype/campaign/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/selling/doctype/campaign/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/selling/doctype/campaign/campaign.js b/erpnext/selling/doctype/campaign/campaign.js
deleted file mode 100644
index 72a90d0..0000000
--- a/erpnext/selling/doctype/campaign/campaign.js
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-// License: GNU General Public License v3. See license.txt
-
-frappe.ui.form.on("Campaign", "refresh", function(frm) {
- erpnext.toggle_naming_series();
- if(frm.doc.__islocal) {
- frm.toggle_display("naming_series", frappe.boot.sysdefaults.campaign_naming_by=="Naming Series");
- }
- else{
- cur_frm.add_custom_button(__("View Leads"), function() {
- frappe.route_options = {"source": "Campaign","campaign_name": frm.doc.name}
- frappe.set_route("List", "Lead");
- }, "fa fa-list", true);
- }
-})
diff --git a/erpnext/selling/doctype/campaign/campaign_dashboard.py b/erpnext/selling/doctype/campaign/campaign_dashboard.py
deleted file mode 100644
index 3cef560..0000000
--- a/erpnext/selling/doctype/campaign/campaign_dashboard.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-
-def get_data():
- return {
- 'fieldname': 'campaign_name',
- 'transactions': [
- {
- 'label': _('Email Campaigns'),
- 'items': ['Email Campaign']
- },
- {
- 'label': _('Social Media Campaigns'),
- 'items': ['Social Media Post']
- }
- ]
- }
diff --git a/erpnext/selling/doctype/campaign/test_campaign.py b/erpnext/selling/doctype/campaign/test_campaign.py
deleted file mode 100644
index 4d062ff..0000000
--- a/erpnext/selling/doctype/campaign/test_campaign.py
+++ /dev/null
@@ -1,7 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
-
-
-import frappe
-test_records = frappe.get_test_records('Campaign')
\ No newline at end of file
diff --git a/erpnext/selling/doctype/campaign/test_records.json b/erpnext/selling/doctype/campaign/test_records.json
deleted file mode 100644
index 625d3b3..0000000
--- a/erpnext/selling/doctype/campaign/test_records.json
+++ /dev/null
@@ -1,10 +0,0 @@
-[
- {
- "campaign_name": "_Test Campaign",
- "doctype": "Campaign"
- },
- {
- "campaign_name": "_Test Campaign 1",
- "doctype": "Campaign"
- }
-]
\ No newline at end of file
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 3b62081..abf146c 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -157,9 +157,7 @@
'''If Customer created from Lead, update lead status to "Converted"
update Customer link in Quotation, Opportunity'''
if self.lead_name:
- lead = frappe.get_doc('Lead', self.lead_name)
- lead.status = 'Converted'
- lead.save()
+ frappe.db.set_value("Lead", self.lead_name, "status", "Converted")
def create_lead_address_contact(self):
if self.lead_name:
@@ -176,12 +174,12 @@
address.append('links', dict(link_doctype='Customer', link_name=self.name))
address.save(ignore_permissions=self.flags.ignore_permissions)
- lead = frappe.db.get_value("Lead", self.lead_name, ["organization_lead", "lead_name", "email_id", "phone", "mobile_no", "gender", "salutation"], as_dict=True)
+ lead = frappe.db.get_value("Lead", self.lead_name, ["company_name", "lead_name", "email_id", "phone", "mobile_no", "gender", "salutation"], as_dict=True)
if not lead.lead_name:
frappe.throw(_("Please mention the Lead Name in Lead {0}").format(self.lead_name))
- if lead.organization_lead:
+ if lead.company_name:
contact_names = frappe.get_all('Dynamic Link', filters={
"parenttype":"Contact",
"link_doctype":"Lead",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index 762b6f1..d31db82 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -38,6 +38,8 @@
"col_break46",
"shipping_address_name",
"shipping_address",
+ "dispatch_address_name",
+ "dispatch_address",
"customer_group",
"territory",
"currency_and_price_list",
@@ -1486,13 +1488,29 @@
"fieldname": "disable_rounded_total",
"fieldtype": "Check",
"label": "Disable Rounded Total"
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "dispatch_address_name",
+ "fieldtype": "Link",
+ "label": "Dispatch Address Name",
+ "options": "Address",
+ "print_hide": 1
+ },
+ {
+ "allow_on_submit": 1,
+ "depends_on": "dispatch_address_name",
+ "fieldname": "dispatch_address",
+ "fieldtype": "Small Text",
+ "label": "Dispatch Address",
+ "read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"links": [],
- "modified": "2021-04-15 23:55:13.439068",
+ "modified": "2021-07-08 21:37:44.177493",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 41f57a3..bba5401 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -670,6 +670,7 @@
"party_account_currency": "party_account_currency",
"payment_terms_template": "payment_terms_template"
},
+ "field_no_map": ["payment_terms_template"],
"validation": {
"docstatus": ["=", 1]
}
@@ -693,6 +694,10 @@
}
}, target_doc, postprocess, ignore_permissions=ignore_permissions)
+ automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms'))
+ if automatically_fetch_payment_terms:
+ doclist.set_payment_schedule()
+
return doclist
@frappe.whitelist()
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 974648d..a0a21ee 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -5,7 +5,7 @@
import unittest
import frappe
import frappe.permissions
-from frappe.utils import flt, add_days, nowdate
+from frappe.utils import flt, add_days, nowdate, getdate
from frappe.core.doctype.user_permission.test_user_permission import create_user
from erpnext.selling.doctype.sales_order.sales_order \
import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired
@@ -673,6 +673,8 @@
so.cancel()
+ dn.load_from_db()
+
self.assertRaises(frappe.CancelledLinkError, dn.submit)
def test_service_type_product_bundle(self):
@@ -1229,7 +1231,38 @@
self.assertRaises(frappe.ValidationError, so.cancel)
+ def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
+ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+ automatically_fetch_payment_terms()
+
+ so = make_sales_order(uom="Nos", do_not_save=1)
+ create_payment_terms_template()
+ so.payment_terms_template = 'Test Receivable Template'
+ so.submit()
+
+ si = create_sales_invoice(qty=10, do_not_save=1)
+ si.items[0].sales_order = so.name
+ si.items[0].so_detail = so.items[0].name
+ si.insert()
+
+ self.assertEqual(so.payment_terms_template, si.payment_terms_template)
+ compare_payment_schedules(self, so, si)
+
+ automatically_fetch_payment_terms(enable=0)
+
+def automatically_fetch_payment_terms(enable=1):
+ accounts_settings = frappe.get_doc("Accounts Settings")
+ accounts_settings.automatically_fetch_payment_terms = enable
+ accounts_settings.save()
+
+def compare_payment_schedules(doc, doc1, doc2):
+ for index, schedule in enumerate(doc1.get('payment_schedule')):
+ doc.assertEqual(schedule.payment_term, doc2.payment_schedule[index].payment_term)
+ doc.assertEqual(getdate(schedule.due_date), doc2.payment_schedule[index].due_date)
+ doc.assertEqual(schedule.invoice_portion, doc2.payment_schedule[index].invoice_portion)
+ doc.assertEqual(schedule.payment_amount, doc2.payment_schedule[index].payment_amount)
def make_sales_order(**args):
so = frappe.new_doc("Sales Order")
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json
index f01934b..717fd9b 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.json
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.json
@@ -6,24 +6,31 @@
"document_type": "Other",
"engine": "InnoDB",
"field_order": [
+ "customer_defaults_section",
"cust_master_name",
- "campaign_naming_by",
"customer_group",
+ "column_break_4",
"territory",
- "selling_price_list",
- "close_opportunity_after_days",
+ "crm_settings_section",
+ "campaign_naming_by",
"default_valid_till",
- "column_break_5",
+ "column_break_9",
+ "close_opportunity_after_days",
+ "item_price_settings_section",
+ "selling_price_list",
+ "column_break_15",
+ "maintain_same_sales_rate",
+ "maintain_same_rate_action",
+ "editable_price_list_rate",
+ "validate_selling_price",
+ "sales_transactions_settings_section",
"so_required",
"dn_required",
"sales_update_frequency",
- "maintain_same_sales_rate",
- "maintain_same_rate_action",
+ "column_break_5",
"role_to_override_stop_action",
- "editable_price_list_rate",
"allow_multiple_items",
"allow_against_multiple_purchase_orders",
- "validate_selling_price",
"hide_tax_id"
],
"fields": [
@@ -116,7 +123,7 @@
"default": "0",
"fieldname": "allow_multiple_items",
"fieldtype": "Check",
- "label": "Allow Item to Be Added Multiple Times in a Transaction"
+ "label": "Allow Item to be Added Multiple Times in a Transaction"
},
{
"default": "0",
@@ -142,7 +149,7 @@
"description": "Configure the action to stop the transaction or just warn if the same rate is not maintained.",
"fieldname": "maintain_same_rate_action",
"fieldtype": "Select",
- "label": "Action If Same Rate is Not Maintained",
+ "label": "Action if Same Rate is Not Maintained",
"mandatory_depends_on": "maintain_same_sales_rate",
"options": "Stop\nWarn"
},
@@ -152,6 +159,38 @@
"fieldtype": "Link",
"label": "Role Allowed to Override Stop Action",
"options": "Role"
+ },
+ {
+ "fieldname": "customer_defaults_section",
+ "fieldtype": "Section Break",
+ "label": "Customer Defaults"
+ },
+ {
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "crm_settings_section",
+ "fieldtype": "Section Break",
+ "label": "CRM Settings"
+ },
+ {
+ "fieldname": "column_break_9",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "item_price_settings_section",
+ "fieldtype": "Section Break",
+ "label": "Item Price Settings"
+ },
+ {
+ "fieldname": "column_break_15",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "sales_transactions_settings_section",
+ "fieldtype": "Section Break",
+ "label": "Transaction Settings"
}
],
"icon": "fa fa-cog",
@@ -159,7 +198,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2021-04-04 20:18:12.814624",
+ "modified": "2021-08-06 22:25:50.119458",
"modified_by": "Administrator",
"module": "Selling",
"name": "Selling Settings",
diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index 6e36d28..a4a4b0e 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -564,7 +564,6 @@
)
set_dynamic_rate_header_width();
- this.scroll_to_item($item_to_update);
function set_dynamic_rate_header_width() {
const rate_cols = Array.from(me.$cart_items_wrapper.find(".item-rate-amount"));
@@ -639,12 +638,6 @@
$($img).parent().replaceWith(`<div class="item-image item-abbr">${item_abbr}</div>`);
}
- scroll_to_item($item) {
- if ($item.length === 0) return;
- const scrollTop = $item.offset().top - this.$cart_items_wrapper.offset().top + this.$cart_items_wrapper.scrollTop();
- this.$cart_items_wrapper.animate({ scrollTop });
- }
-
update_selector_value_in_cart_item(selector, value, item) {
const $item_to_update = this.get_cart_item(item);
$item_to_update.attr(`data-${selector}`, escape(value));
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index f1a166b..63306ad 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -198,6 +198,7 @@
const is_cash_shortcuts_invisible = !this.$payment_modes.find('.cash-shortcuts').is(':visible');
this.attach_cash_shortcuts(frm.doc);
!is_cash_shortcuts_invisible && this.$payment_modes.find('.cash-shortcuts').css('display', 'grid');
+ this.render_payment_mode_dom();
});
frappe.ui.form.on('POS Invoice', 'loyalty_amount', (frm) => {
diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.css b/erpnext/selling/page/sales_funnel/sales_funnel.css
index 89e904f..455d37c 100644
--- a/erpnext/selling/page/sales_funnel/sales_funnel.css
+++ b/erpnext/selling/page/sales_funnel/sales_funnel.css
@@ -1,3 +1,4 @@
.funnel-wrapper {
margin: 15px;
+ width: 100%;
}
\ No newline at end of file
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index eb02867..f515baf 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -26,7 +26,7 @@
}
};
});
- }
+ }
setup_queries() {
var me = this;
@@ -85,7 +85,7 @@
refresh() {
super.refresh();
-
+
frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
this.frm.toggle_display("customer_name",
@@ -114,6 +114,10 @@
erpnext.utils.set_taxes_from_address(this.frm, "shipping_address_name", "customer_address", "shipping_address_name");
}
+ dispatch_address_name() {
+ erpnext.utils.get_address_display(this.frm, "dispatch_address_name", "dispatch_address");
+ }
+
sales_partner() {
this.apply_pricing_rule();
}
diff --git a/erpnext/selling/workspace/retail/retail.json b/erpnext/selling/workspace/retail/retail.json
index e20f834..9d2e6ca 100644
--- a/erpnext/selling/workspace/retail/retail.json
+++ b/erpnext/selling/workspace/retail/retail.json
@@ -1,22 +1,27 @@
{
- "category": "Domains",
+ "category": "",
"charts": [],
+ "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Point Of Sale\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings & Configurations\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loyalty Program\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Opening & Closing\", \"col\": 4}}]",
"creation": "2020-03-02 17:18:32.505616",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "retail",
"idx": 0,
- "is_standard": 1,
+ "is_default": 0,
+ "is_standard": 0,
"label": "Retail",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Settings & Configurations",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -25,6 +30,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Point-of-Sale Profile",
+ "link_count": 0,
"link_to": "POS Profile",
"link_type": "DocType",
"onboard": 1,
@@ -35,6 +41,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "POS Settings",
+ "link_count": 0,
"link_to": "POS Settings",
"link_type": "DocType",
"onboard": 0,
@@ -44,6 +51,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loyalty Program",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -52,6 +60,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loyalty Program",
+ "link_count": 0,
"link_to": "Loyalty Program",
"link_type": "DocType",
"onboard": 0,
@@ -62,6 +71,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Loyalty Point Entry",
+ "link_count": 0,
"link_to": "Loyalty Point Entry",
"link_type": "DocType",
"onboard": 0,
@@ -71,6 +81,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Opening & Closing",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -79,6 +90,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "POS Opening Entry",
+ "link_count": 0,
"link_to": "POS Opening Entry",
"link_type": "DocType",
"onboard": 0,
@@ -89,20 +101,26 @@
"hidden": 0,
"is_query_report": 0,
"label": "POS Closing Entry",
+ "link_count": 0,
"link_to": "POS Closing Entry",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2020-12-01 13:38:36.758038",
+ "modified": "2021-08-05 12:16:01.840988",
"modified_by": "Administrator",
"module": "Selling",
"name": "Retail",
+ "onboarding": "",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
"restrict_to_domain": "Retail",
+ "roles": [],
+ "sequence_id": 22,
"shortcuts": [
{
"doc_view": "",
@@ -110,5 +128,6 @@
"link_to": "point-of-sale",
"type": "Page"
}
- ]
+ ],
+ "title": "Retail"
}
\ No newline at end of file
diff --git a/erpnext/selling/workspace/selling/selling.json b/erpnext/selling/workspace/selling/selling.json
index 879034a..345187f 100644
--- a/erpnext/selling/workspace/selling/selling.json
+++ b/erpnext/selling/workspace/selling/selling.json
@@ -1,5 +1,5 @@
{
- "category": "Modules",
+ "category": "",
"charts": [
{
"chart_name": "Sales Order Trends",
@@ -7,22 +7,27 @@
}
],
"charts_label": "Selling ",
+ "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Selling\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Sales Order Trends\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Quick Access\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Order\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Analytics\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Order Analysis\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Selling\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Items and Pricing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}]",
"creation": "2020-01-28 11:49:12.092882",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
- "hide_custom": 1,
+ "for_user": "",
+ "hide_custom": 0,
"icon": "sell",
"idx": 0,
- "is_standard": 1,
+ "is_default": 0,
+ "is_standard": 0,
"label": "Selling",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Selling",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -31,6 +36,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Customer",
+ "link_count": 0,
"link_to": "Customer",
"link_type": "DocType",
"onboard": 1,
@@ -41,6 +47,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Quotation",
+ "link_count": 0,
"link_to": "Quotation",
"link_type": "DocType",
"onboard": 1,
@@ -51,6 +58,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Sales Order",
+ "link_count": 0,
"link_to": "Sales Order",
"link_type": "DocType",
"onboard": 1,
@@ -61,6 +69,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Sales Invoice",
+ "link_count": 0,
"link_to": "Sales Invoice",
"link_type": "DocType",
"onboard": 1,
@@ -71,6 +80,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Blanket Order",
+ "link_count": 0,
"link_to": "Blanket Order",
"link_type": "DocType",
"onboard": 1,
@@ -81,6 +91,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Sales Partner",
+ "link_count": 0,
"link_to": "Sales Partner",
"link_type": "DocType",
"onboard": 0,
@@ -91,6 +102,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Sales Person",
+ "link_count": 0,
"link_to": "Sales Person",
"link_type": "DocType",
"onboard": 0,
@@ -100,6 +112,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Items and Pricing",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -108,6 +121,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item",
+ "link_count": 0,
"link_to": "Item",
"link_type": "DocType",
"onboard": 1,
@@ -118,6 +132,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item Price",
+ "link_count": 0,
"link_to": "Item Price",
"link_type": "DocType",
"onboard": 1,
@@ -128,6 +143,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Price List",
+ "link_count": 0,
"link_to": "Price List",
"link_type": "DocType",
"onboard": 1,
@@ -138,6 +154,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item Group",
+ "link_count": 0,
"link_to": "Item Group",
"link_type": "DocType",
"onboard": 1,
@@ -148,6 +165,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Product Bundle",
+ "link_count": 0,
"link_to": "Product Bundle",
"link_type": "DocType",
"onboard": 0,
@@ -158,6 +176,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Promotional Scheme",
+ "link_count": 0,
"link_to": "Promotional Scheme",
"link_type": "DocType",
"onboard": 0,
@@ -168,6 +187,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Pricing Rule",
+ "link_count": 0,
"link_to": "Pricing Rule",
"link_type": "DocType",
"onboard": 0,
@@ -178,6 +198,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Shipping Rule",
+ "link_count": 0,
"link_to": "Shipping Rule",
"link_type": "DocType",
"onboard": 0,
@@ -188,6 +209,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Coupon Code",
+ "link_count": 0,
"link_to": "Coupon Code",
"link_type": "DocType",
"onboard": 0,
@@ -197,6 +219,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Settings",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -205,6 +228,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Selling Settings",
+ "link_count": 0,
"link_to": "Selling Settings",
"link_type": "DocType",
"onboard": 0,
@@ -215,6 +239,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Terms and Conditions Template",
+ "link_count": 0,
"link_to": "Terms and Conditions",
"link_type": "DocType",
"onboard": 1,
@@ -225,6 +250,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Sales Taxes and Charges Template",
+ "link_count": 0,
"link_to": "Sales Taxes and Charges Template",
"link_type": "DocType",
"onboard": 1,
@@ -235,6 +261,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Lead Source",
+ "link_count": 0,
"link_to": "Lead Source",
"link_type": "DocType",
"onboard": 0,
@@ -245,6 +272,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Customer Group",
+ "link_count": 0,
"link_to": "Customer Group",
"link_type": "DocType",
"onboard": 0,
@@ -255,6 +283,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Contact",
+ "link_count": 0,
"link_to": "Contact",
"link_type": "DocType",
"onboard": 0,
@@ -265,6 +294,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Address",
+ "link_count": 0,
"link_to": "Address",
"link_type": "DocType",
"onboard": 0,
@@ -275,6 +305,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Territory",
+ "link_count": 0,
"link_to": "Territory",
"link_type": "DocType",
"onboard": 0,
@@ -285,6 +316,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Campaign",
+ "link_count": 0,
"link_to": "Campaign",
"link_type": "DocType",
"onboard": 0,
@@ -294,6 +326,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Key Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -302,6 +335,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Sales Analytics",
+ "link_count": 0,
"link_to": "Sales Analytics",
"link_type": "Report",
"onboard": 1,
@@ -312,6 +346,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Sales Order Analysis",
+ "link_count": 0,
"link_to": "Sales Order Analysis",
"link_type": "Report",
"onboard": 1,
@@ -322,6 +357,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Sales Funnel",
+ "link_count": 0,
"link_to": "sales-funnel",
"link_type": "Page",
"onboard": 1,
@@ -332,6 +368,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Sales Order Trends",
+ "link_count": 0,
"link_to": "Sales Order Trends",
"link_type": "Report",
"onboard": 0,
@@ -342,6 +379,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Quotation Trends",
+ "link_count": 0,
"link_to": "Quotation Trends",
"link_type": "Report",
"onboard": 0,
@@ -352,6 +390,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Customer Acquisition and Loyalty",
+ "link_count": 0,
"link_to": "Customer Acquisition and Loyalty",
"link_type": "Report",
"onboard": 0,
@@ -362,6 +401,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Inactive Customers",
+ "link_count": 0,
"link_to": "Inactive Customers",
"link_type": "Report",
"onboard": 0,
@@ -372,6 +412,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Sales Person-wise Transaction Summary",
+ "link_count": 0,
"link_to": "Sales Person-wise Transaction Summary",
"link_type": "Report",
"onboard": 0,
@@ -382,6 +423,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Item-wise Sales History",
+ "link_count": 0,
"link_to": "Item-wise Sales History",
"link_type": "Report",
"onboard": 0,
@@ -391,6 +433,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Other Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -399,6 +442,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Lead Details",
+ "link_count": 0,
"link_to": "Lead Details",
"link_type": "Report",
"onboard": 0,
@@ -409,6 +453,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Customer Addresses And Contacts",
+ "link_count": 0,
"link_to": "Address And Contacts",
"link_type": "Report",
"onboard": 0,
@@ -419,6 +464,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Available Stock for Packing Items",
+ "link_count": 0,
"link_to": "Available Stock for Packing Items",
"link_type": "Report",
"onboard": 0,
@@ -429,6 +475,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Pending SO Items For Purchase Request",
+ "link_count": 0,
"link_to": "Pending SO Items For Purchase Request",
"link_type": "Report",
"onboard": 0,
@@ -439,6 +486,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Delivery Note Trends",
+ "link_count": 0,
"link_to": "Delivery Note Trends",
"link_type": "Report",
"onboard": 0,
@@ -449,6 +497,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Sales Invoice Trends",
+ "link_count": 0,
"link_to": "Sales Invoice Trends",
"link_type": "Report",
"onboard": 0,
@@ -459,6 +508,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Customer Credit Balance",
+ "link_count": 0,
"link_to": "Customer Credit Balance",
"link_type": "Report",
"onboard": 0,
@@ -469,6 +519,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Customers Without Any Sales Transactions",
+ "link_count": 0,
"link_to": "Customers Without Any Sales Transactions",
"link_type": "Report",
"onboard": 0,
@@ -479,6 +530,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Sales Partners Commission",
+ "link_count": 0,
"link_to": "Sales Partners Commission",
"link_type": "Report",
"onboard": 0,
@@ -489,6 +541,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Territory Target Variance Based On Item Group",
+ "link_count": 0,
"link_to": "Territory Target Variance Based On Item Group",
"link_type": "Report",
"onboard": 0,
@@ -499,6 +552,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Sales Person Target Variance Based On Item Group",
+ "link_count": 0,
"link_to": "Sales Person Target Variance Based On Item Group",
"link_type": "Report",
"onboard": 0,
@@ -509,20 +563,26 @@
"hidden": 0,
"is_query_report": 1,
"label": "Sales Partner Target Variance Based On Item Group",
+ "link_count": 0,
"link_to": "Sales Partner Target Variance based on Item Group",
"link_type": "Report",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2020-12-01 13:38:35.971277",
+ "modified": "2021-08-05 12:16:01.990702",
"modified_by": "Administrator",
"module": "Selling",
"name": "Selling",
"onboarding": "Selling",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 23,
"shortcuts": [
{
"color": "Grey",
@@ -559,5 +619,6 @@
"type": "Dashboard"
}
],
- "shortcuts_label": "Quick Access"
+ "shortcuts_label": "Quick Access",
+ "title": "Selling"
}
\ No newline at end of file
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 8755125..95cbf51 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -108,6 +108,9 @@
frappe.flags.country_change = True
self.create_default_accounts()
self.create_default_warehouses()
+
+ if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": self.name}):
+ self.create_default_cost_center()
if frappe.flags.country_change:
install_country_fixtures(self.name, self.country)
@@ -117,9 +120,6 @@
from erpnext.setup.setup_wizard.operations.install_fixtures import install_post_company_fixtures
install_post_company_fixtures(frappe._dict({'company_name': self.name}))
- if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": self.name}):
- self.create_default_cost_center()
-
if not frappe.local.flags.ignore_chart_of_accounts:
self.set_default_accounts()
if self.default_cash_account:
diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
index c5c01c5..4ff2dd7 100644
--- a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
+++ b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
@@ -62,12 +62,12 @@
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30", "for_selling")
self.assertEqual(exchange_rate, 62.9)
-
- # Exchange rate as on 15th Dec, 2015, should be fetched from fixer.io
+
+ # Exchange rate as on 15th Dec, 2015
self.clear_cache()
exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15", "for_selling")
self.assertFalse(exchange_rate == 60)
- self.assertEqual(flt(exchange_rate, 3), 66.894)
+ self.assertEqual(flt(exchange_rate, 3), 66.999)
def test_exchange_rate_strict(self):
# strict currency settings
@@ -77,28 +77,17 @@
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-01", "for_buying")
self.assertEqual(exchange_rate, 60.0)
- # Will fetch from fixer.io
self.clear_cache()
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying")
- self.assertEqual(flt(exchange_rate, 3), 67.79)
+ self.assertEqual(flt(exchange_rate, 3), 67.235)
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30", "for_selling")
self.assertEqual(exchange_rate, 62.9)
- # Exchange rate as on 15th Dec, 2015, should be fetched from fixer.io
+ # Exchange rate as on 15th Dec, 2015
self.clear_cache()
exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15", "for_buying")
- self.assertEqual(flt(exchange_rate, 3), 66.894)
-
- exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-10", "for_selling")
- self.assertEqual(exchange_rate, 65.1)
-
- # NGN is not available on fixer.io so these should return 0
- exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-09", "for_selling")
- self.assertEqual(exchange_rate, 0)
-
- exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-11", "for_selling")
- self.assertEqual(exchange_rate, 0)
+ self.assertEqual(flt(exchange_rate, 3), 66.999)
def test_exchange_rate_strict_switched(self):
# Start with allow_stale is True
@@ -111,4 +100,4 @@
# Will fetch from fixer.io
self.clear_cache()
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying")
- self.assertEqual(flt(exchange_rate, 3), 67.79)
+ self.assertEqual(flt(exchange_rate, 3), 67.235)
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json
index 9313f95..23e5947 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json
@@ -54,7 +54,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2021-05-08 23:13:48.049879",
+ "modified": "2021-08-04 20:15:59.071493",
"modified_by": "Administrator",
"module": "Setup",
"name": "Transaction Deletion Record",
@@ -70,6 +70,7 @@
"report": 1,
"role": "System Manager",
"share": 1,
+ "submit": 1,
"write": 1
}
],
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
index 691d331..8a49155 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
@@ -18,7 +18,7 @@
doctypes_to_be_ignored_list = get_doctypes_to_be_ignored()
for doctype in self.doctypes_to_be_ignored:
if doctype.doctype_name not in doctypes_to_be_ignored_list:
- frappe.throw(_("DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it. "),
+ frappe.throw(_("DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it."),
title=_("Not Allowed"))
def before_submit(self):
@@ -31,7 +31,7 @@
clear_notifications()
self.delete_company_transactions()
- def populate_doctypes_to_be_ignored_table(self):
+ def populate_doctypes_to_be_ignored_table(self):
doctypes_to_be_ignored_list = get_doctypes_to_be_ignored()
for doctype in doctypes_to_be_ignored_list:
self.append('doctypes_to_be_ignored', {
@@ -74,7 +74,7 @@
doctypes_to_be_ignored_list = self.get_doctypes_to_be_ignored_list()
docfields = self.get_doctypes_with_company_field(doctypes_to_be_ignored_list)
- tables = self.get_all_child_doctypes()
+ tables = self.get_all_child_doctypes()
for docfield in docfields:
if docfield['parent'] != self.doctype:
no_of_docs = self.get_number_of_docs_linked_with_specified_company(docfield['parent'], docfield['fieldname'])
@@ -90,7 +90,7 @@
naming_series = frappe.db.get_value('DocType', docfield['parent'], 'autoname')
if naming_series:
if '#' in naming_series:
- self.update_naming_series(naming_series, docfield['parent'])
+ self.update_naming_series(naming_series, docfield['parent'])
def get_doctypes_to_be_ignored_list(self):
singles = frappe.get_all('DocType', filters = {'issingle': 1}, pluck = 'name')
@@ -101,9 +101,9 @@
return doctypes_to_be_ignored_list
def get_doctypes_with_company_field(self, doctypes_to_be_ignored_list):
- docfields = frappe.get_all('DocField',
+ docfields = frappe.get_all('DocField',
filters = {
- 'fieldtype': 'Link',
+ 'fieldtype': 'Link',
'options': 'Company',
'parent': ['not in', doctypes_to_be_ignored_list]},
fields=['parent', 'fieldname'])
@@ -121,7 +121,7 @@
self.append('doctypes', {
'doctype_name' : doctype,
'no_of_docs' : no_of_docs
- })
+ })
def delete_child_tables(self, doctype, company_fieldname):
parent_docs_to_be_deleted = frappe.get_all(doctype, {
@@ -129,7 +129,7 @@
}, pluck = 'name')
child_tables = frappe.get_all('DocField', filters = {
- 'fieldtype': 'Table',
+ 'fieldtype': 'Table',
'parent': doctype
}, pluck = 'options')
diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py
index c375dac..bacada9 100644
--- a/erpnext/setup/setup_wizard/operations/taxes_setup.py
+++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py
@@ -124,7 +124,8 @@
account_data = tax_row.get('account_head')
tax_row_defaults = {
'category': 'Total',
- 'charge_type': 'On Net Total'
+ 'charge_type': 'On Net Total',
+ 'cost_center': frappe.db.get_value('Company', company_name, 'cost_center')
}
if doctype == 'Purchase Taxes and Charges Template':
@@ -183,16 +184,6 @@
doc.insert(ignore_permissions=True)
return doc
-def make_tax_category(tax_category):
- """ Make tax category based on title if not already created """
- doctype = 'Tax Category'
- if not frappe.db.exists(doctype, tax_category['title']):
- tax_category['doctype'] = doctype
- doc = frappe.get_doc(tax_category)
- doc.flags.ignore_links = True
- doc.flags.ignore_validate = True
- doc.insert(ignore_permissions=True)
-
def get_or_create_account(company_name, account):
"""
Check if account already exists. If not, create it.
@@ -284,7 +275,7 @@
return tax_group_name
-def make_tax_catgory(tax_category):
+def make_tax_category(tax_category):
doctype = 'Tax Category'
if isinstance(tax_category, str):
tax_category = {'title': tax_category}
diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py
index d5dbd4c..e49259e 100644
--- a/erpnext/setup/utils.py
+++ b/erpnext/setup/utils.py
@@ -93,21 +93,21 @@
try:
cache = frappe.cache()
- key = "currency_exchange_rate_{0}:{1}:{2}".format(transaction_date,from_currency, to_currency)
+ key = "currency_exchange_rate_{0}:{1}:{2}".format(transaction_date, from_currency, to_currency)
value = cache.get(key)
if not value:
import requests
- api_url = "https://frankfurter.app/{0}".format(transaction_date)
+ api_url = "https://api.exchangerate.host/convert"
response = requests.get(api_url, params={
- "base": from_currency,
- "symbols": to_currency
+ "date": transaction_date,
+ "from": from_currency,
+ "to": to_currency
})
# expire in 6 hours
response.raise_for_status()
- value = response.json()["rates"][to_currency]
-
- cache.set_value(key, value, expires_in_sec=6 * 60 * 60)
+ value = response.json()["result"]
+ cache.setex(name=key, time=21600, value=flt(value))
return flt(value)
except:
frappe.log_error(title="Get Exchange Rate")
diff --git a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
index 6ca3d63..ef4b050 100644
--- a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
+++ b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
@@ -1,27 +1,35 @@
{
- "category": "Modules",
+ "category": "",
"charts": [],
+ "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Projects Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Accounts Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"HR Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Selling Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Buying Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Support Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Shopping Cart Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Portal Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Manufacturing Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Education Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Hotel Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Healthcare Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Domain Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Products Settings\", \"col\": 4}}]",
"creation": "2020-03-12 14:47:51.166455",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
- "extends": "Settings",
- "extends_another_page": 1,
+ "extends": "",
+ "extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
- "icon": "settings",
+ "icon": "setting",
"idx": 0,
"is_default": 0,
- "is_standard": 1,
+ "is_standard": 0,
"label": "ERPNext Settings",
"links": [],
- "modified": "2021-06-12 01:58:11.399566",
+ "modified": "2021-08-05 12:15:59.052327",
"modified_by": "Administrator",
"module": "Setup",
"name": "ERPNext Settings",
+ "onboarding": "",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 12,
"shortcuts": [
{
"icon": "project",
@@ -118,5 +126,6 @@
"link_to": "Products Settings",
"type": "DocType"
}
- ]
-}
+ ],
+ "title": "ERPNext Settings"
+}
\ No newline at end of file
diff --git a/erpnext/setup/workspace/home/home.json b/erpnext/setup/workspace/home/home.json
index 1576d5a..cc9569f 100644
--- a/erpnext/setup/workspace/home/home.json
+++ b/erpnext/setup/workspace/home/home.json
@@ -1,23 +1,27 @@
{
- "category": "Modules",
+ "category": "",
"charts": [],
+ "content": "[{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"level\":4,\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customer\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Supplier\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Leaderboard\",\"col\":4}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"level\":4,\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Accounting\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Stock\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Human Resources\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"CRM\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Data Import and Settings\",\"col\":4}}]",
"creation": "2020-01-23 13:46:38.833076",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "getting-started",
"idx": 0,
"is_default": 0,
- "is_standard": 1,
+ "is_standard": 0,
"label": "Home",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Accounting",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -26,6 +30,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Chart of Accounts",
+ "link_count": 0,
"link_to": "Account",
"link_type": "DocType",
"onboard": 1,
@@ -36,6 +41,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Company",
+ "link_count": 0,
"link_to": "Company",
"link_type": "DocType",
"onboard": 1,
@@ -46,6 +52,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Customer",
+ "link_count": 0,
"link_to": "Customer",
"link_type": "DocType",
"onboard": 1,
@@ -56,6 +63,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Supplier",
+ "link_count": 0,
"link_to": "Supplier",
"link_type": "DocType",
"onboard": 1,
@@ -65,6 +73,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Stock",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -73,6 +82,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item",
+ "link_count": 0,
"link_to": "Item",
"link_type": "DocType",
"onboard": 1,
@@ -83,6 +93,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Warehouse",
+ "link_count": 0,
"link_to": "Warehouse",
"link_type": "DocType",
"onboard": 1,
@@ -93,6 +104,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Brand",
+ "link_count": 0,
"link_to": "Brand",
"link_type": "DocType",
"onboard": 1,
@@ -103,6 +115,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Unit of Measure (UOM)",
+ "link_count": 0,
"link_to": "UOM",
"link_type": "DocType",
"onboard": 1,
@@ -113,6 +126,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Stock Reconciliation",
+ "link_count": 0,
"link_to": "Stock Reconciliation",
"link_type": "DocType",
"onboard": 1,
@@ -122,6 +136,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Human Resources",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -130,6 +145,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee",
+ "link_count": 0,
"link_to": "Employee",
"link_type": "DocType",
"onboard": 1,
@@ -140,6 +156,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Employee Attendance Tool",
+ "link_count": 0,
"link_to": "Employee Attendance Tool",
"link_type": "DocType",
"onboard": 1,
@@ -150,6 +167,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Salary Structure",
+ "link_count": 0,
"link_to": "Salary Structure",
"link_type": "DocType",
"onboard": 1,
@@ -159,6 +177,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "CRM",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -167,6 +186,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Lead",
+ "link_count": 0,
"link_to": "Lead",
"link_type": "DocType",
"onboard": 1,
@@ -177,6 +197,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Customer Group",
+ "link_count": 0,
"link_to": "Customer Group",
"link_type": "DocType",
"onboard": 1,
@@ -187,6 +208,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Territory",
+ "link_count": 0,
"link_to": "Territory",
"link_type": "DocType",
"onboard": 1,
@@ -196,6 +218,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Data Import and Settings",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -204,6 +227,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Import Data",
+ "link_count": 0,
"link_to": "Data Import",
"link_type": "DocType",
"onboard": 1,
@@ -214,6 +238,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Opening Invoice Creation Tool",
+ "link_count": 0,
"link_to": "Opening Invoice Creation Tool",
"link_type": "DocType",
"onboard": 1,
@@ -224,6 +249,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Chart of Accounts Importer",
+ "link_count": 0,
"link_to": "Chart of Accounts Importer",
"link_type": "DocType",
"onboard": 1,
@@ -234,6 +260,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Letter Head",
+ "link_count": 0,
"link_to": "Letter Head",
"link_type": "DocType",
"onboard": 1,
@@ -244,19 +271,26 @@
"hidden": 0,
"is_query_report": 0,
"label": "Email Account",
+ "link_count": 0,
"link_to": "Email Account",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
}
],
- "modified": "2021-04-19 15:48:44.089927",
+ "modified": "2021-08-10 15:33:20.704740",
"modified_by": "Administrator",
"module": "Setup",
"name": "Home",
+ "onboarding": "",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
- "pin_to_top": 1,
+ "pin_to_top": 0,
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 1,
"shortcuts": [
{
"label": "Item",
@@ -283,5 +317,6 @@
"link_to": "leaderboard",
"type": "Page"
}
- ]
+ ],
+ "title": "Home"
}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index b6eef6c..b37ae3f 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -162,19 +162,19 @@
out = float(frappe.db.sql("""select sum(actual_qty)
from `tabStock Ledger Entry`
- where warehouse=%s and batch_no=%s {0}""".format(cond),
+ where is_cancelled = 0 and warehouse=%s and batch_no=%s {0}""".format(cond),
(warehouse, batch_no))[0][0] or 0)
if batch_no and not warehouse:
out = frappe.db.sql('''select warehouse, sum(actual_qty) as qty
from `tabStock Ledger Entry`
- where batch_no=%s
+ where is_cancelled = 0 and batch_no=%s
group by warehouse''', batch_no, as_dict=1)
if not batch_no and item_code and warehouse:
out = frappe.db.sql('''select batch_no, sum(actual_qty) as qty
from `tabStock Ledger Entry`
- where item_code = %s and warehouse=%s
+ where is_cancelled = 0 and item_code = %s and warehouse=%s
group by batch_no''', (item_code, warehouse), as_dict=1)
return out
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index cbd272d..a85a022 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -269,11 +269,14 @@
batch2 = create_batch('_Test Batch Price Item', 300, 1)
batch3 = create_batch('_Test Batch Price Item', 400, 0)
+ company = "_Test Company with perpetual inventory"
+ currency = frappe.get_cached_value("Company", company, "default_currency")
+
args = frappe._dict({
"item_code": "_Test Batch Price Item",
- "company": "_Test Company with perpetual inventory",
+ "company": company,
"price_list": "_Test Price List",
- "currency": "_Test Currency",
+ "currency": currency,
"doctype": "Sales Invoice",
"conversion_rate": 1,
"price_list_currency": "_Test Currency",
@@ -333,4 +336,4 @@
except frappe.DuplicateEntryError:
batch = frappe.get_doc("Batch", args.batch_id)
- return batch
\ No newline at end of file
+ return batch
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index f20e76f..dbfeb4a 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -32,6 +32,8 @@
"contact_info",
"shipping_address_name",
"shipping_address",
+ "dispatch_address_name",
+ "dispatch_address",
"contact_person",
"contact_display",
"contact_mobile",
@@ -1282,13 +1284,28 @@
"fieldname": "disable_rounded_total",
"fieldtype": "Check",
"label": "Disable Rounded Total"
+ },
+ {
+ "fieldname": "dispatch_address_name",
+ "fieldtype": "Link",
+ "label": "Dispatch Address Name",
+ "options": "Address",
+ "print_hide": 1
+ },
+ {
+ "depends_on": "dispatch_address_name",
+ "fieldname": "dispatch_address",
+ "fieldtype": "Small Text",
+ "label": "Dispatch Address",
+ "print_hide": 1,
+ "read_only": 1
}
],
"icon": "fa fa-truck",
"idx": 146,
"is_submittable": 1,
"links": [],
- "modified": "2021-06-11 19:27:30.901112",
+ "modified": "2021-07-08 21:37:20.802652",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 4808e94..f99a01b 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -503,6 +503,10 @@
}
}, target_doc, set_missing_values)
+ automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms'))
+ if automatically_fetch_payment_terms:
+ doc.set_payment_schedule()
+
return doc
@frappe.whitelist()
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index f981aeb..756825e 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -17,7 +17,8 @@
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, SerialNoWarehouseError
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation \
import create_stock_reconciliation, set_valuation_method
-from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order, create_dn_against_so
+from erpnext.selling.doctype.sales_order.test_sales_order \
+ import make_sales_order, create_dn_against_so, automatically_fetch_payment_terms, compare_payment_schedules
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
from erpnext.stock.doctype.item.test_item import make_item
@@ -759,6 +760,32 @@
self.assertTrue("TESTBATCH" in dn.packed_items[0].batch_no, "Batch number not added in packed item")
+ def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
+ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+
+ automatically_fetch_payment_terms()
+
+ so = make_sales_order(uom="Nos", do_not_save=1)
+ create_payment_terms_template()
+ so.payment_terms_template = 'Test Receivable Template'
+ so.submit()
+
+ dn = create_dn_against_so(so.name, delivered_qty=10)
+
+ si = create_sales_invoice(qty=10, do_not_save=1)
+ si.items[0].delivery_note= dn.name
+ si.items[0].dn_detail = dn.items[0].name
+ si.items[0].sales_order = so.name
+ si.items[0].so_detail = so.items[0].name
+
+ si.insert()
+ si.submit()
+
+ self.assertEqual(so.payment_terms_template, si.payment_terms_template)
+ compare_payment_schedules(self, so, si)
+
+ automatically_fetch_payment_terms(enable=0)
def create_delivery_note(**args):
dn = frappe.new_doc("Delivery Note")
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index c68cd01..c5bc9f1 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -100,10 +100,11 @@
frm.add_custom_button(__('Duplicate'), function() {
var new_item = frappe.model.copy_doc(frm.doc);
- if(new_item.item_name===new_item.item_code) {
+ // Duplicate item could have different name, causing "copy paste" error.
+ if (new_item.item_name===new_item.item_code) {
new_item.item_name = null;
}
- if(new_item.description===new_item.description) {
+ if (new_item.item_code===new_item.description || new_item.item_code===new_item.description) {
new_item.description = null;
}
frappe.set_route('Form', 'Item', new_item.name);
@@ -137,20 +138,6 @@
frm.toggle_reqd('customer', frm.doc.is_customer_provided_item ? 1:0);
},
- gst_hsn_code: function(frm) {
- if((!frm.doc.taxes || !frm.doc.taxes.length) && frm.doc.gst_hsn_code) {
- frappe.db.get_doc("GST HSN Code", frm.doc.gst_hsn_code).then(hsn_doc => {
- $.each(hsn_doc.taxes || [], function(i, tax) {
- let a = frappe.model.add_child(cur_frm.doc, 'Item Tax', 'taxes');
- a.item_tax_template = tax.item_tax_template;
- a.tax_category = tax.tax_category;
- a.valid_from = tax.valid_from;
- frm.refresh_field('taxes');
- });
- });
- }
- },
-
is_fixed_asset: function(frm) {
// set serial no to false & toggles its visibility
frm.set_value('has_serial_no', 0);
@@ -186,8 +173,6 @@
item_code: function(frm) {
if(!frm.doc.item_name)
frm.set_value("item_name", frm.doc.item_code);
- if(!frm.doc.description)
- frm.set_value("description", frm.doc.item_code);
},
is_stock_item: function(frm) {
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 42cc67c..614c53a 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -123,6 +123,7 @@
self.cant_change()
self.update_show_in_website()
self.validate_item_tax_net_rate_range()
+ set_item_tax_from_hsn_code(self)
if not self.is_new():
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
@@ -1305,3 +1306,7 @@
def on_doctype_update():
# since route is a Text column, it needs a length for indexing
frappe.db.add_index("Item", ["route(500)"])
+
+@erpnext.allow_regional
+def set_item_tax_from_hsn_code(item):
+ pass
\ No newline at end of file
diff --git a/erpnext/stock/doctype/item/regional/india.js b/erpnext/stock/doctype/item/regional/india.js
new file mode 100644
index 0000000..77ae51f
--- /dev/null
+++ b/erpnext/stock/doctype/item/regional/india.js
@@ -0,0 +1,15 @@
+frappe.ui.form.on('Item', {
+ gst_hsn_code: function(frm) {
+ if ((!frm.doc.taxes || !frm.doc.taxes.length) && frm.doc.gst_hsn_code) {
+ frappe.db.get_doc("GST HSN Code", frm.doc.gst_hsn_code).then(hsn_doc => {
+ $.each(hsn_doc.taxes || [], function(i, tax) {
+ let a = frappe.model.add_child(cur_frm.doc, 'Item Tax', 'taxes');
+ a.item_tax_template = tax.item_tax_template;
+ a.tax_category = tax.tax_category;
+ a.valid_from = tax.valid_from;
+ frm.refresh_field('taxes');
+ });
+ });
+ }
+ },
+});
\ No newline at end of file
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 922049f..7a9985d 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -83,14 +83,17 @@
make_test_objects("Item Price")
+ company = "_Test Company"
+ currency = frappe.get_cached_value("Company", company, "default_currency")
+
details = get_item_details({
"item_code": "_Test Item",
- "company": "_Test Company",
+ "company": company,
"price_list": "_Test Price List",
- "currency": "_Test Currency",
+ "currency": currency,
"doctype": "Sales Order",
"conversion_rate": 1,
- "price_list_currency": "_Test Currency",
+ "price_list_currency": currency,
"plc_conversion_rate": 1,
"order_type": "Sales",
"customer": "_Test Customer",
diff --git a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
index 9b1a47e..5de45cb 100644
--- a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
+++ b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
@@ -47,7 +47,8 @@
"description": "Simple Python formula applied on Reading fields.<br> Numeric eg. 1: <b>reading_1 > 0.2 and reading_1 < 0.5</b><br>\nNumeric eg. 2: <b>mean > 3.5</b> (mean of populated fields)<br>\nValue based eg.: <b>reading_value in (\"A\", \"B\", \"C\")</b>",
"fieldname": "acceptance_formula",
"fieldtype": "Code",
- "label": "Acceptance Criteria Formula"
+ "label": "Acceptance Criteria Formula",
+ "options": "PythonExpression"
},
{
"default": "0",
@@ -89,7 +90,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2021-02-04 18:50:02.056173",
+ "modified": "2021-08-06 15:08:20.911338",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Quality Inspection Parameter",
diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
index 32b08f6..cb09d93 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
@@ -11,6 +11,7 @@
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.accounts.doctype.account.test_account import create_account
+from erpnext.assets.doctype.asset.test_asset import create_asset_category, create_fixed_asset_item
class TestLandedCostVoucher(unittest.TestCase):
def test_landed_cost_voucher(self):
@@ -250,6 +251,39 @@
self.assertEqual(entry.credit, amounts[0])
self.assertEqual(entry.credit_in_account_currency, amounts[1])
+ def test_asset_lcv(self):
+ "Check if LCV for an Asset updates the Assets Gross Purchase Amount correctly."
+ frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "CWIP Account - _TC")
+
+ if not frappe.db.exists("Asset Category", "Computers"):
+ create_asset_category()
+
+ if not frappe.db.exists("Item", "Macbook Pro"):
+ create_fixed_asset_item()
+
+ pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=50000)
+
+ # check if draft asset was created
+ assets = frappe.db.get_all('Asset', filters={'purchase_receipt': pr.name})
+ self.assertEqual(len(assets), 1)
+
+ lcv = make_landed_cost_voucher(
+ company = pr.company,
+ receipt_document_type = "Purchase Receipt",
+ receipt_document=pr.name,
+ charges=80,
+ expense_account="Expenses Included In Valuation - _TC")
+
+ lcv.save()
+ lcv.submit()
+
+ # lcv updates amount in draft asset
+ self.assertEqual(frappe.db.get_value("Asset", assets[0].name, "gross_purchase_amount"), 50080)
+
+ # tear down
+ lcv.cancel()
+ pr.cancel()
+
def make_landed_cost_voucher(** args):
args = frappe._dict(args)
ref_doc = frappe.get_doc(args.receipt_document_type, args.receipt_document)
@@ -268,7 +302,7 @@
lcv.set("taxes", [{
"description": "Shipping Charges",
- "expense_account": "Expenses Included In Valuation - TCP1",
+ "expense_account": args.expense_account or "Expenses Included In Valuation - TCP1",
"amount": args.charges
}])
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index 3ad9909..026b85e 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -162,8 +162,15 @@
from `tabStock Entry Detail` where material_request = %s
and material_request_item = %s and docstatus = 1""",
(self.name, d.name))[0][0])
+ mr_qty_allowance = frappe.db.get_single_value('Stock Settings', 'mr_qty_allowance')
- if d.ordered_qty and d.ordered_qty > d.stock_qty:
+ if mr_qty_allowance:
+ allowed_qty = d.qty + (d.qty * (mr_qty_allowance/100))
+ if d.ordered_qty and d.ordered_qty > allowed_qty:
+ frappe.throw(_("The total Issue / Transfer quantity {0} in Material Request {1} \
+ cannot be greater than allowed requested quantity {2} for Item {3}").format(d.ordered_qty, d.parent, allowed_qty, d.item_code))
+
+ elif d.ordered_qty and d.ordered_qty > d.stock_qty:
frappe.throw(_("The total Issue / Transfer quantity {0} in Material Request {1} \
cannot be greater than requested quantity {2} for Item {3}").format(d.ordered_qty, d.parent, d.qty, d.item_code))
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index 72a3a5e..b4776ba 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -329,6 +329,58 @@
self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
+ def test_over_transfer_qty_allowance(self):
+ mr = frappe.new_doc('Material Request')
+ mr.company = "_Test Company"
+ mr.scheduled_date = today()
+ mr.append('items',{
+ "item_code": "_Test FG Item",
+ "item_name": "_Test FG Item",
+ "qty": 10,
+ "schedule_date": today(),
+ "uom": "_Test UOM 1",
+ "warehouse": "_Test Warehouse - _TC"
+ })
+
+ mr.material_request_type = "Material Transfer"
+ mr.insert()
+ mr.submit()
+
+ frappe.db.set_value('Stock Settings', None, 'mr_qty_allowance', 20)
+
+ # map a stock entry
+
+ se_doc = make_stock_entry(mr.name)
+ se_doc.update({
+ "posting_date": today(),
+ "posting_time": "00:00",
+ })
+ se_doc.get("items")[0].update({
+ "qty": 13,
+ "transfer_qty": 12.0,
+ "s_warehouse": "_Test Warehouse - _TC",
+ "t_warehouse": "_Test Warehouse 1 - _TC",
+ "basic_rate": 1.0
+ })
+
+ # make available the qty in _Test Warehouse 1 before transfer
+ sr = frappe.new_doc("Stock Reconciliation")
+ sr.company = "_Test Company"
+ sr.purpose = "Opening Stock"
+ sr.append('items', {
+ "item_code": "_Test FG Item",
+ "warehouse": "_Test Warehouse - _TC",
+ "qty": 20,
+ "valuation_rate": 0.01
+ })
+ sr.insert()
+ sr.submit()
+ se = frappe.copy_doc(se_doc)
+ se.insert()
+ self.assertRaises(frappe.ValidationError)
+ se.items[0].qty = 12
+ se.submit()
+
def test_completed_qty_for_over_transfer(self):
existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index e795742..516ae43 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -239,6 +239,7 @@
and sle.`item_code`=%(item_code)s
and sle.`company` = %(company)s
and batch.disabled = 0
+ and sle.is_cancelled=0
and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s
{warehouse_condition}
GROUP BY
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 41800e3..ece6d6f 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -290,8 +290,16 @@
and warehouse_account_name == supplier_warehouse_account:
continue
- self.add_gl_entry(gl_entries, warehouse_account_name, d.cost_center, stock_value_diff, 0.0, remarks,
- stock_rbnb, account_currency=warehouse_account_currency, item=d)
+ self.add_gl_entry(
+ gl_entries=gl_entries,
+ account=warehouse_account_name,
+ cost_center=d.cost_center,
+ debit=stock_value_diff,
+ credit=0.0,
+ remarks=remarks,
+ against_account=stock_rbnb,
+ account_currency=warehouse_account_currency,
+ item=d)
# GL Entry for from warehouse or Stock Received but not billed
# Intentionally passed negative debit amount to avoid incorrect GL Entry validation
@@ -304,9 +312,17 @@
account = warehouse_account[d.from_warehouse]['account'] \
if d.from_warehouse else stock_rbnb
- self.add_gl_entry(gl_entries, account, d.cost_center,
- -1 * flt(d.base_net_amount, d.precision("base_net_amount")), 0.0, remarks, warehouse_account_name,
- debit_in_account_currency=-1 * credit_amount, account_currency=credit_currency, item=d)
+ self.add_gl_entry(
+ gl_entries=gl_entries,
+ account=account,
+ cost_center=d.cost_center,
+ debit=-1 * flt(d.base_net_amount, d.precision("base_net_amount")),
+ credit=0.0,
+ remarks=remarks,
+ against_account=warehouse_account_name,
+ debit_in_account_currency=-1 * credit_amount,
+ account_currency=credit_currency,
+ item=d)
# check if the exchange rate has changed
if d.get('purchase_invoice'):
@@ -317,13 +333,29 @@
discrepancy_caused_by_exchange_rate_difference = (d.qty * d.net_rate) * \
(exchange_rate_map[d.purchase_invoice] - self.conversion_rate)
- self.add_gl_entry(gl_entries, account, d.cost_center, 0.0, discrepancy_caused_by_exchange_rate_difference,
- remarks, self.supplier, debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
- account_currency=credit_currency, item=d)
+ self.add_gl_entry(
+ gl_entries=gl_entries,
+ account=account,
+ cost_center=d.cost_center,
+ debit=0.0,
+ credit=discrepancy_caused_by_exchange_rate_difference,
+ remarks=remarks,
+ against_account=self.supplier,
+ debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
+ account_currency=credit_currency,
+ item=d)
- self.add_gl_entry(gl_entries, self.get_company_default("exchange_gain_loss_account"), d.cost_center, discrepancy_caused_by_exchange_rate_difference, 0.0,
- remarks, self.supplier, debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
- account_currency=credit_currency, item=d)
+ self.add_gl_entry(
+ gl_entries=gl_entries,
+ account=self.get_company_default("exchange_gain_loss_account"),
+ cost_center=d.cost_center,
+ debit=discrepancy_caused_by_exchange_rate_difference,
+ credit=0.0,
+ remarks=remarks,
+ against_account=self.supplier,
+ debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
+ account_currency=credit_currency,
+ item=d)
# Amount added through landed-cos-voucher
if d.landed_cost_voucher_amount and landed_cost_entries:
@@ -332,14 +364,31 @@
credit_amount = (flt(amount["base_amount"]) if (amount["base_amount"] or
account_currency!=self.company_currency) else flt(amount["amount"]))
- self.add_gl_entry(gl_entries, account, d.cost_center, 0.0, credit_amount, remarks,
- warehouse_account_name, credit_in_account_currency=flt(amount["amount"]),
- account_currency=account_currency, project=d.project, item=d)
+ self.add_gl_entry(
+ gl_entries=gl_entries,
+ account=account,
+ cost_center=d.cost_center,
+ debit=0.0,
+ credit=credit_amount,
+ remarks=remarks,
+ against_account=warehouse_account_name,
+ credit_in_account_currency=flt(amount["amount"]),
+ account_currency=account_currency,
+ project=d.project,
+ item=d)
# sub-contracting warehouse
if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse):
- self.add_gl_entry(gl_entries, supplier_warehouse_account, d.cost_center, 0.0, flt(d.rm_supp_cost),
- remarks, warehouse_account_name, account_currency=supplier_warehouse_account_currency, item=d)
+ self.add_gl_entry(
+ gl_entries=gl_entries,
+ account=supplier_warehouse_account,
+ cost_center=d.cost_center,
+ debit=0.0,
+ credit=flt(d.rm_supp_cost),
+ remarks=remarks,
+ against_account=warehouse_account_name,
+ account_currency=supplier_warehouse_account_currency,
+ item=d)
# divisional loss adjustment
valuation_amount_as_per_doc = flt(d.base_net_amount, d.precision("base_net_amount")) + \
@@ -352,12 +401,21 @@
if self.is_return or flt(d.item_tax_amount):
loss_account = expenses_included_in_valuation
else:
- loss_account = self.get_company_default("default_expense_account")
+ loss_account = self.get_company_default("default_expense_account", ignore_validation=True) or stock_rbnb
cost_center = d.cost_center or frappe.get_cached_value("Company", self.company, "cost_center")
- self.add_gl_entry(gl_entries, loss_account, cost_center, divisional_loss, 0.0, remarks,
- warehouse_account_name, account_currency=credit_currency, project=d.project, item=d)
+ self.add_gl_entry(
+ gl_entries=gl_entries,
+ account=loss_account,
+ cost_center=cost_center,
+ debit=divisional_loss,
+ credit=0.0,
+ remarks=remarks,
+ against_account=warehouse_account_name,
+ account_currency=credit_currency,
+ project=d.project,
+ item=d)
elif d.warehouse not in warehouse_with_no_account or \
d.rejected_warehouse not in warehouse_with_no_account:
@@ -368,12 +426,30 @@
debit_currency = get_account_currency(d.expense_account)
remarks = self.get("remarks") or _("Accounting Entry for Service")
- self.add_gl_entry(gl_entries, service_received_but_not_billed_account, d.cost_center, 0.0, d.amount,
- remarks, d.expense_account, account_currency=credit_currency, project=d.project,
+ self.add_gl_entry(
+ gl_entries=gl_entries,
+ account=service_received_but_not_billed_account,
+ cost_center=d.cost_center,
+ debit=0.0,
+ credit=d.amount,
+ remarks=remarks,
+ against_account=d.expense_account,
+ account_currency=credit_currency,
+ project=d.project,
voucher_detail_no=d.name, item=d)
- self.add_gl_entry(gl_entries, d.expense_account, d.cost_center, d.amount, 0.0, remarks, service_received_but_not_billed_account,
- account_currency = debit_currency, project=d.project, voucher_detail_no=d.name, item=d)
+ self.add_gl_entry(
+ gl_entries=gl_entries,
+ account=d.expense_account,
+ cost_center=d.cost_center,
+ debit=d.amount,
+ credit=0.0,
+ remarks=remarks,
+ against_account=service_received_but_not_billed_account,
+ account_currency = debit_currency,
+ project=d.project,
+ voucher_detail_no=d.name,
+ item=d)
if warehouse_with_no_account:
frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" +
@@ -423,8 +499,15 @@
applicable_amount = negative_expense_to_be_booked * (valuation_tax[tax.name] / total_valuation_amount)
amount_including_divisional_loss -= applicable_amount
- self.add_gl_entry(gl_entries, account, tax.cost_center, 0.0, applicable_amount, self.remarks or _("Accounting Entry for Stock"),
- against_account, item=tax)
+ self.add_gl_entry(
+ gl_entries=gl_entries,
+ account=account,
+ cost_center=tax.cost_center,
+ debit=0.0,
+ credit=applicable_amount,
+ remarks=self.remarks or _("Accounting Entry for Stock"),
+ against_account=against_account,
+ item=tax)
i += 1
@@ -436,7 +519,7 @@
"cost_center": cost_center,
"debit": debit,
"credit": credit,
- "against_account": against_account,
+ "against": against_account,
"remarks": remarks,
}
@@ -477,15 +560,31 @@
# debit cwip account
debit_in_account_currency = (base_asset_amount
if cwip_account_currency == self.company_currency else asset_amount)
- self.add_gl_entry(gl_entries, cwip_account, item.cost_center, base_asset_amount, 0.0, remarks,
- arbnb_account, debit_in_account_currency=debit_in_account_currency, item=item)
+ self.add_gl_entry(
+ gl_entries=gl_entries,
+ account=cwip_account,
+ cost_center=item.cost_center,
+ debit=base_asset_amount,
+ credit=0.0,
+ remarks=remarks,
+ against_account=arbnb_account,
+ debit_in_account_currency=debit_in_account_currency,
+ item=item)
asset_rbnb_currency = get_account_currency(arbnb_account)
# credit arbnb account
credit_in_account_currency = (base_asset_amount
if asset_rbnb_currency == self.company_currency else asset_amount)
- self.add_gl_entry(gl_entries, arbnb_account, item.cost_center, 0.0, base_asset_amount, remarks,
- cwip_account, credit_in_account_currency=credit_in_account_currency, item=item)
+ self.add_gl_entry(
+ gl_entries=gl_entries,
+ account=arbnb_account,
+ cost_center=item.cost_center,
+ debit=0.0,
+ credit=base_asset_amount,
+ remarks=remarks,
+ against_account=cwip_account,
+ credit_in_account_currency=credit_in_account_currency,
+ item=item)
def add_lcv_gl_entries(self, item, gl_entries):
expenses_included_in_asset_valuation = self.get_company_default("expenses_included_in_asset_valuation")
@@ -498,11 +597,27 @@
remarks = self.get("remarks") or _("Accounting Entry for Stock")
- self.add_gl_entry(gl_entries, expenses_included_in_asset_valuation, item.cost_center, 0.0, flt(item.landed_cost_voucher_amount),
- remarks, asset_account, project=item.project, item=item)
+ self.add_gl_entry(
+ gl_entries=gl_entries,
+ account=expenses_included_in_asset_valuation,
+ cost_center=item.cost_center,
+ debit=0.0,
+ credit=flt(item.landed_cost_voucher_amount),
+ remarks=remarks,
+ against_account=asset_account,
+ project=item.project,
+ item=item)
- self.add_gl_entry(gl_entries, asset_account, item.cost_center, 0.0, flt(item.landed_cost_voucher_amount),
- remarks, expenses_included_in_asset_valuation, project=item.project, item=item)
+ self.add_gl_entry(
+ gl_entries=gl_entries,
+ account=asset_account,
+ cost_center=item.cost_center,
+ debit=flt(item.landed_cost_voucher_amount),
+ credit=0.0,
+ remarks=remarks,
+ against_account=expenses_included_in_asset_valuation,
+ project=item.project,
+ item=item)
def update_assets(self, item, valuation_rate):
assets = frappe.db.get_all('Asset',
@@ -619,6 +734,7 @@
doc.run_method("onload")
doc.run_method("set_missing_values")
doc.run_method("calculate_taxes_and_totals")
+ doc.set_payment_schedule()
def update_item(source_doc, target_doc, source_parent):
target_doc.qty, returned_qty = get_pending_qty(source_doc)
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index dbba21f..05df161 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -23,9 +23,7 @@
def test_reverse_purchase_receipt_sle(self):
- frappe.db.set_value('UOM', '_Test UOM', 'must_be_whole_number', 0)
-
- pr = make_purchase_receipt(qty=0.5)
+ pr = make_purchase_receipt(qty=0.5, item_code="_Test Item Home Desktop 200")
sl_entry = frappe.db.get_all("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
"voucher_no": pr.name}, ['actual_qty'])
@@ -41,8 +39,6 @@
self.assertEqual(len(sl_entry_cancelled), 2)
self.assertEqual(sl_entry_cancelled[1].actual_qty, -0.5)
- frappe.db.set_value('UOM', '_Test UOM', 'must_be_whole_number', 1)
-
def test_make_purchase_invoice(self):
if not frappe.db.exists('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice'):
frappe.get_doc({
@@ -328,18 +324,8 @@
pr1.submit()
self.assertRaises(frappe.ValidationError, pr2.submit)
+ frappe.db.rollback()
- pr1.cancel()
- se.cancel()
- se1.cancel()
- se2.cancel()
- se3.cancel()
- po.reload()
- pr2.load_from_db()
- pr2.cancel()
-
- po.load_from_db()
- po.cancel()
def test_serial_no_supplier(self):
pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1)
@@ -1044,7 +1030,7 @@
'account': srbnb_account,
'voucher_detail_no': pr.items[1].name
}, pluck="name")
-
+
# check if the entries are not merged into one
# seperate entries should be made since voucher_detail_no is different
self.assertEqual(len(item_one_gl_entry), 1)
@@ -1055,13 +1041,13 @@
def test_purchase_receipt_with_exchange_rate_difference(self):
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice as create_purchase_invoice
from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import make_purchase_receipt as create_purchase_receipt
-
+
pi = create_purchase_invoice(company="_Test Company with perpetual inventory",
cost_center = "Main - TCP1",
warehouse = "Stores - TCP1",
expense_account ="_Test Account Cost for Goods Sold - TCP1",
currency = "USD", conversion_rate = 70)
-
+
pr = create_purchase_receipt(pi.name)
pr.conversion_rate = 80
pr.items[0].purchase_invoice = pi.name
@@ -1079,6 +1065,33 @@
self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount)
+ def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
+ from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+ from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order, make_pr_against_po
+ from erpnext.selling.doctype.sales_order.test_sales_order import automatically_fetch_payment_terms, compare_payment_schedules
+
+ automatically_fetch_payment_terms()
+
+ po = create_purchase_order(qty=10, rate=100, do_not_save=1)
+ create_payment_terms_template()
+ po.payment_terms_template = 'Test Receivable Template'
+ po.submit()
+
+ pr = make_pr_against_po(po.name, received_qty=10)
+
+ pi = make_purchase_invoice(qty=10, rate=100, do_not_save=1)
+ pi.items[0].purchase_receipt = pr.name
+ pi.items[0].pr_detail = pr.items[0].name
+ pi.items[0].purchase_order = po.name
+ pi.items[0].po_detail = po.items[0].name
+ pi.insert()
+
+ # self.assertEqual(po.payment_terms_template, pi.payment_terms_template)
+ compare_payment_schedules(self, po, pi)
+
+ automatically_fetch_payment_terms(enable=0)
+
def get_sl_entries(voucher_type, voucher_no):
return frappe.db.sql(""" select actual_qty, warehouse, stock_value_difference
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js
index b3e4286..4cd40bf 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js
@@ -29,13 +29,50 @@
};
});
}
+
+ frm.trigger('setup_realtime_progress');
},
+
+ setup_realtime_progress: function(frm) {
+ frappe.realtime.on('item_reposting_progress', data => {
+ if (frm.doc.name !== data.name) {
+ return;
+ }
+
+ if (frm.doc.status == 'In Progress') {
+ frm.doc.current_index = data.current_index;
+ frm.doc.items_to_be_repost = data.items_to_be_repost;
+
+ frm.dashboard.reset();
+ frm.trigger('show_reposting_progress');
+ }
+ });
+ },
+
refresh: function(frm) {
if (frm.doc.status == "Failed" && frm.doc.docstatus==1) {
frm.add_custom_button(__('Restart'), function () {
frm.trigger("restart_reposting");
}).addClass("btn-primary");
}
+
+ frm.trigger('show_reposting_progress');
+ },
+
+ show_reposting_progress: function(frm) {
+ var bars = [];
+
+ let total_count = frm.doc.items_to_be_repost ? JSON.parse(frm.doc.items_to_be_repost).length : 0;
+ let progress = flt(cint(frm.doc.current_index) / total_count * 100, 2) || 0.5;
+ var title = __('Reposting Completed {0}%', [progress]);
+
+ bars.push({
+ 'title': title,
+ 'width': progress + '%',
+ 'progress_class': 'progress-bar-success'
+ });
+
+ frm.dashboard.add_progress(__('Reposting Progress'), bars);
},
restart_reposting: function(frm) {
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json
index 071fc86..a800bf8 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json
@@ -21,7 +21,10 @@
"allow_zero_rate",
"amended_from",
"error_section",
- "error_log"
+ "error_log",
+ "items_to_be_repost",
+ "distinct_item_and_warehouse",
+ "current_index"
],
"fields": [
{
@@ -142,12 +145,39 @@
"fieldname": "allow_zero_rate",
"fieldtype": "Check",
"label": "Allow Zero Rate"
+ },
+ {
+ "fieldname": "items_to_be_repost",
+ "fieldtype": "Code",
+ "hidden": 1,
+ "label": "Items to Be Repost",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "distinct_item_and_warehouse",
+ "fieldtype": "Code",
+ "hidden": 1,
+ "label": "Distinct Item and Warehouse",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "current_index",
+ "fieldtype": "Int",
+ "hidden": 1,
+ "label": "Current Index",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-12-10 07:52:12.476589",
+ "modified": "2021-07-22 18:59:43.057878",
"modified_by": "Administrator",
"module": "Stock",
"name": "Repost Item Valuation",
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index 5f31d9c..b22759d 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -81,7 +81,7 @@
def repost_sl_entries(doc):
if doc.based_on == 'Transaction':
repost_future_sle(voucher_type=doc.voucher_type, voucher_no=doc.voucher_no,
- allow_negative_stock=doc.allow_negative_stock, via_landed_cost_voucher=doc.via_landed_cost_voucher)
+ allow_negative_stock=doc.allow_negative_stock, via_landed_cost_voucher=doc.via_landed_cost_voucher, doc=doc)
else:
repost_future_sle(args=[frappe._dict({
"item_code": doc.item_code,
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index bad7b60..70312bc 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -165,8 +165,14 @@
)
ORDER BY
posting_date desc, posting_time desc, creation desc""",
- (self.item_code, self.company,
- serial_no, serial_no+'\n%', '%\n'+serial_no, '%\n'+serial_no+'\n%'), as_dict=1):
+ (
+ self.item_code, self.company,
+ serial_no,
+ serial_no+'\n%',
+ '%\n'+serial_no,
+ '%\n'+serial_no+'\n%'
+ ),
+ as_dict=1):
if serial_no.upper() in get_serial_nos(sle.serial_no):
if cint(sle.actual_qty) > 0:
sle_dict.setdefault("incoming", []).append(sle)
diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.py b/erpnext/stock/doctype/serial_no/test_serial_no.py
index cde7fe0..b9a58cf 100644
--- a/erpnext/stock/doctype/serial_no/test_serial_no.py
+++ b/erpnext/stock/doctype/serial_no/test_serial_no.py
@@ -174,5 +174,23 @@
self.assertEqual(sn_doc.warehouse, "_Test Warehouse - _TC")
self.assertEqual(sn_doc.purchase_document_no, se.name)
+ def test_serial_no_sanitation(self):
+ "Test if Serial No input is sanitised before entering the DB."
+ item_code = "_Test Serialized Item"
+ test_records = frappe.get_test_records('Stock Entry')
+
+ se = frappe.copy_doc(test_records[0])
+ se.get("items")[0].item_code = item_code
+ se.get("items")[0].qty = 3
+ se.get("items")[0].serial_no = " _TS1, _TS2 , _TS3 "
+ se.get("items")[0].transfer_qty = 3
+ se.set_stock_entry_type()
+ se.insert()
+ se.submit()
+
+ self.assertEqual(se.get("items")[0].serial_no, "_TS1\n_TS2\n_TS3")
+
+ frappe.db.rollback()
+
def tearDown(self):
frappe.db.rollback()
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index fcb6f0f..7b31d2f 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -76,6 +76,7 @@
self.validate_difference_account()
self.set_job_card_data()
self.set_purpose_for_stock_entry()
+ self.clean_serial_nos()
self.validate_duplicate_serial_no()
if not self.from_bom:
@@ -1184,7 +1185,7 @@
wo = frappe.get_doc("Work Order", self.work_order)
wo_items = frappe.get_all('Work Order Item',
filters={'parent': self.work_order},
- fields=["item_code", "required_qty", "consumed_qty", "transferred_qty"]
+ fields=["item_code", "source_warehouse", "required_qty", "consumed_qty", "transferred_qty"]
)
work_order_qty = wo.material_transferred_for_manufacturing or wo.qty
@@ -1204,7 +1205,7 @@
if qty > 0:
self.add_to_stock_entry_detail({
item.item_code: {
- "from_warehouse": wo.wip_warehouse,
+ "from_warehouse": wo.wip_warehouse or item.source_warehouse,
"to_warehouse": "",
"qty": qty,
"item_name": item.item_name,
@@ -1789,7 +1790,7 @@
from `tabBatch` b, `tabStock Ledger Entry` sle
where b.expiry_date <= %s
and b.expiry_date is not NULL
- and b.batch_id = sle.batch_no
+ and b.batch_id = sle.batch_no and sle.is_cancelled = 0
group by sle.warehouse, sle.item_code, sle.batch_no""",(nowdate()), as_dict=1)
@frappe.whitelist()
@@ -1856,4 +1857,4 @@
supplied_item.total_supplied_qty = flt(supplied_item.supplied_qty) - flt(supplied_item.returned_qty)
- return supplied_item_details
\ No newline at end of file
+ return supplied_item_details
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index 93482e8..be1f00e 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -55,12 +55,12 @@
"sum(actual_qty)") or 0
frappe.db.set_value("Batch", self.batch_no, "batch_qty", batch_qty)
- #check for item quantity available in stock
def actual_amt_check(self):
+ """Validate that qty at warehouse for selected batch is >=0"""
if self.batch_no and not self.get("allow_negative_stock"):
batch_bal_after_transaction = flt(frappe.db.sql("""select sum(actual_qty)
from `tabStock Ledger Entry`
- where warehouse=%s and item_code=%s and batch_no=%s""",
+ where is_cancelled =0 and warehouse=%s and item_code=%s and batch_no=%s""",
(self.warehouse, self.item_code, self.batch_no))[0][0])
if batch_bal_after_transaction < 0:
@@ -107,7 +107,7 @@
self.stock_uom = item_det.stock_uom
def check_stock_frozen_date(self):
- stock_settings = frappe.get_doc('Stock Settings', 'Stock Settings')
+ stock_settings = frappe.get_cached_doc('Stock Settings')
if stock_settings.stock_frozen_upto:
if (getdate(self.posting_date) <= getdate(stock_settings.stock_frozen_upto)
@@ -152,7 +152,7 @@
last_transaction_time = frappe.db.sql("""
select MAX(timestamp(posting_date, posting_time)) as posting_time
from `tabStock Ledger Entry`
- where docstatus = 1 and item_code = %s
+ where docstatus = 1 and is_cancelled = 0 and item_code = %s
and warehouse = %s""", (self.item_code, self.warehouse))[0][0]
cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index 4540954..84f65a0 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -56,25 +56,40 @@
},
get_items: function(frm) {
- let fields = [{
- label: 'Warehouse', fieldname: 'warehouse', fieldtype: 'Link', options: 'Warehouse', reqd: 1,
- "get_query": function() {
- return {
- "filters": {
- "company": frm.doc.company,
- }
- };
+ let fields = [
+ {
+ label: 'Warehouse',
+ fieldname: 'warehouse',
+ fieldtype: 'Link',
+ options: 'Warehouse',
+ reqd: 1,
+ "get_query": function() {
+ return {
+ "filters": {
+ "company": frm.doc.company,
+ }
+ };
+ }
+ },
+ {
+ label: "Item Code",
+ fieldname: "item_code",
+ fieldtype: "Link",
+ options: "Item",
+ "get_query": function() {
+ return {
+ "filters": {
+ "disabled": 0,
+ }
+ };
+ }
+ },
+ {
+ label: __("Ignore Empty Stock"),
+ fieldname: "ignore_empty_stock",
+ fieldtype: "Check"
}
- }, {
- label: "Item Code", fieldname: "item_code", fieldtype: "Link", options: "Item",
- "get_query": function() {
- return {
- "filters": {
- "disabled": 0,
- }
- };
- }
- }];
+ ];
frappe.prompt(fields, function(data) {
frappe.call({
@@ -84,22 +99,21 @@
posting_date: frm.doc.posting_date,
posting_time: frm.doc.posting_time,
company: frm.doc.company,
- item_code: data.item_code
+ item_code: data.item_code,
+ ignore_empty_stock: data.ignore_empty_stock
},
callback: function(r) {
+ if (r.exc || !r.message || !r.message.length) return;
+
frm.clear_table("items");
- for (var i=0; i<r.message.length; i++) {
- var d = frm.add_child("items");
- $.extend(d, r.message[i]);
- if (!d.qty) {
- d.qty = 0;
- }
+ r.message.forEach((row) => {
+ let item = frm.add_child("items");
+ $.extend(item, row);
- if (!d.valuation_rate) {
- d.valuation_rate = 0;
- }
- }
+ item.qty = item.qty || 0;
+ item.valuation_rate = item.valuation_rate || 0;
+ });
frm.refresh_field("items");
}
});
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 9875491..cda7c1d 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -31,6 +31,7 @@
self.validate_expense_account()
self.validate_customer_provided_item()
self.set_zero_value_for_customer_provided_items()
+ self.clean_serial_nos()
self.set_total_qty_and_amount()
self.validate_putaway_capacity()
@@ -483,7 +484,8 @@
self._cancel()
@frappe.whitelist()
-def get_items(warehouse, posting_date, posting_time, company, item_code=None):
+def get_items(warehouse, posting_date, posting_time, company, item_code=None, ignore_empty_stock=False):
+ ignore_empty_stock = cint(ignore_empty_stock)
items = [frappe._dict({
'item_code': item_code,
'warehouse': warehouse
@@ -497,18 +499,24 @@
for d in items:
if d.item_code in itemwise_batch_data:
- stock_bal = get_stock_balance(d.item_code, d.warehouse,
- posting_date, posting_time, with_valuation_rate=True)
+ valuation_rate = get_stock_balance(d.item_code, d.warehouse,
+ posting_date, posting_time, with_valuation_rate=True)[1]
for row in itemwise_batch_data.get(d.item_code):
- args = get_item_data(row, row.qty, stock_bal[1])
+ if ignore_empty_stock and not row.qty:
+ continue
+
+ args = get_item_data(row, row.qty, valuation_rate)
res.append(args)
else:
stock_bal = get_stock_balance(d.item_code, d.warehouse, posting_date, posting_time,
with_valuation_rate=True , with_serial_no=cint(d.has_serial_no))
+ qty, valuation_rate, serial_no = stock_bal[0], stock_bal[1], stock_bal[2] if cint(d.has_serial_no) else ''
- args = get_item_data(d, stock_bal[0], stock_bal[1],
- stock_bal[2] if cint(d.has_serial_no) else '')
+ if ignore_empty_stock and not stock_bal[0]:
+ continue
+
+ args = get_item_data(d, qty, valuation_rate, serial_no)
res.append(args)
@@ -516,24 +524,44 @@
def get_items_for_stock_reco(warehouse, company):
lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
- items = frappe.db.sql("""
- select i.name as item_code, i.item_name, bin.warehouse as warehouse, i.has_serial_no, i.has_batch_no
- from tabBin bin, tabItem i
- where i.name=bin.item_code and IFNULL(i.disabled, 0) = 0 and i.is_stock_item = 1
- and i.has_variants = 0 and exists(
- select name from `tabWarehouse` where lft >= %s and rgt <= %s and name=bin.warehouse
- )
- """, (lft, rgt), as_dict=1)
+ items = frappe.db.sql(f"""
+ select
+ i.name as item_code, i.item_name, bin.warehouse as warehouse, i.has_serial_no, i.has_batch_no
+ from
+ tabBin bin, tabItem i
+ where
+ i.name = bin.item_code
+ and IFNULL(i.disabled, 0) = 0
+ and i.is_stock_item = 1
+ and i.has_variants = 0
+ and exists(
+ select name from `tabWarehouse` where lft >= {lft} and rgt <= {rgt} and name = bin.warehouse
+ )
+ """, as_dict=1)
items += frappe.db.sql("""
- select i.name as item_code, i.item_name, id.default_warehouse as warehouse, i.has_serial_no, i.has_batch_no
- from tabItem i, `tabItem Default` id
- where i.name = id.parent
- and exists(select name from `tabWarehouse` where lft >= %s and rgt <= %s and name=id.default_warehouse)
- and i.is_stock_item = 1 and i.has_variants = 0 and IFNULL(i.disabled, 0) = 0 and id.company=%s
+ select
+ i.name as item_code, i.item_name, id.default_warehouse as warehouse, i.has_serial_no, i.has_batch_no
+ from
+ tabItem i, `tabItem Default` id
+ where
+ i.name = id.parent
+ and exists(
+ select name from `tabWarehouse` where lft >= %s and rgt <= %s and name=id.default_warehouse
+ )
+ and i.is_stock_item = 1
+ and i.has_variants = 0
+ and IFNULL(i.disabled, 0) = 0
+ and id.company = %s
group by i.name
""", (lft, rgt, company), as_dict=1)
+ # remove duplicates
+ # check if item-warehouse key extracted from each entry exists in set iw_keys
+ # and update iw_keys
+ iw_keys = set()
+ items = [item for item in items if [(item.item_code, item.warehouse) not in iw_keys, iw_keys.add((item.item_code, item.warehouse))][0]]
+
return items
def get_item_data(row, qty, valuation_rate, serial_no=None):
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index 2a9dcfb..f75cb56 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -18,6 +18,7 @@
"section_break_9",
"over_delivery_receipt_allowance",
"role_allowed_to_over_deliver_receive",
+ "mr_qty_allowance",
"column_break_12",
"auto_insert_price_list_rate_if_missing",
"allow_negative_stock",
@@ -283,6 +284,12 @@
"fieldtype": "Select",
"label": "Action If Quality Inspection Is Rejected",
"options": "Stop\nWarn"
+ },
+ {
+ "description": "The percentage you are allowed to transfer more against the quantity ordered. For example, if you have ordered 100 units, and your Allowance is 10%, then you are allowed transfer 110 units.",
+ "fieldname": "mr_qty_allowance",
+ "fieldtype": "Float",
+ "label": "Over Transfer Allowance"
}
],
"icon": "icon-cog",
@@ -290,7 +297,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2021-07-10 16:17:42.159829",
+ "modified": "2021-06-28 17:02:26.683002",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Settings",
@@ -310,4 +317,4 @@
"sort_field": "modified",
"sort_order": "ASC",
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 06d0f7e..a0fbcec 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -74,9 +74,7 @@
update_party_blanket_order(args, out)
- if not doc or cint(doc.get('is_return')) == 0:
- # get price list rate only if the invoice is not a credit or debit note
- get_price_list_rate(args, item, out)
+ out.update(get_price_list_rate(args, item))
if args.customer and cint(args.is_pos):
out.update(get_pos_profile_item_details(args.company, args, update_data=True))
@@ -314,8 +312,8 @@
"transaction_date": args.get("transaction_date"),
"against_blanket_order": args.get("against_blanket_order"),
"bom_no": item.get("default_bom"),
- "weight_per_unit": args.get("weight_per_unit") or item.get("weight_per_unit"),
- "weight_uom": args.get("weight_uom") or item.get("weight_uom")
+ "weight_per_unit": item.get("weight_per_unit"),
+ "weight_uom": item.get("weight_uom")
})
if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"):
@@ -644,7 +642,10 @@
or item_group.get("default_supplier")
or brand.get("default_supplier"))
-def get_price_list_rate(args, item_doc, out):
+def get_price_list_rate(args, item_doc, out=None):
+ if out is None:
+ out = frappe._dict()
+
meta = frappe.get_meta(args.parenttype or args.doctype)
if meta.get_field("currency") or args.get('currency'):
@@ -657,17 +658,17 @@
if meta.get_field("currency"):
validate_conversion_rate(args, meta)
- price_list_rate = get_price_list_rate_for(args, item_doc.name) or 0
+ price_list_rate = get_price_list_rate_for(args, item_doc.name)
# variant
- if not price_list_rate and item_doc.variant_of:
+ if price_list_rate is None and item_doc.variant_of:
price_list_rate = get_price_list_rate_for(args, item_doc.variant_of)
# insert in database
- if not price_list_rate:
+ if price_list_rate is None:
if args.price_list and args.rate:
insert_item_price(args)
- return {}
+ return out
out.price_list_rate = flt(price_list_rate) * flt(args.plc_conversion_rate) \
/ flt(args.conversion_rate)
@@ -677,6 +678,8 @@
out.update(get_last_purchase_details(item_doc.name,
args.name, args.conversion_rate))
+ return out
+
def insert_item_price(args):
"""Insert Item Price if Price List and Price List Rate are specified and currency is the same"""
if frappe.db.get_value("Price List", args.price_list, "currency", cache=True) == args.currency \
@@ -1079,9 +1082,8 @@
}
def apply_price_list_on_item(args):
- item_details = frappe._dict()
item_doc = frappe.get_doc("Item", args.item_code)
- get_price_list_rate(args, item_doc, item_details)
+ item_details = get_price_list_rate(args, item_doc)
item_details.update(get_pricing_rule_for_item(args, item_details.price_list_rate))
diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/__init__.py b/erpnext/stock/report/cogs_by_item_group/__init__.py
similarity index 100%
copy from erpnext/erpnext_integrations/doctype/shopify_log/__init__.py
copy to erpnext/stock/report/cogs_by_item_group/__init__.py
diff --git a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js
new file mode 100644
index 0000000..d7c50a6
--- /dev/null
+++ b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js
@@ -0,0 +1,31 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+
+frappe.query_reports["COGS By Item Group"] = {
+ filters: [
+ {
+ label: __("Company"),
+ fieldname: "company",
+ fieldtype: "Link",
+ options: "Company",
+ mandatory: true,
+ default: frappe.defaults.get_user_default("Company"),
+ },
+ {
+ label: __("From Date"),
+ fieldname: "from_date",
+ fieldtype: "Date",
+ mandatory: true,
+ default: frappe.datetime.year_start(),
+ },
+ {
+ label: __("To Date"),
+ fieldname: "to_date",
+ fieldtype: "Date",
+ mandatory: true,
+ default: frappe.datetime.get_today(),
+ },
+ ]
+};
diff --git a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.json b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.json
new file mode 100644
index 0000000..a14adf8
--- /dev/null
+++ b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.json
@@ -0,0 +1,32 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-06-02 18:59:19.830928",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-06-02 18:59:55.470621",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "COGS By Item Group",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "GL Entry",
+ "report_name": "COGS By Item Group",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Accounts User"
+ },
+ {
+ "role": "Accounts Manager"
+ },
+ {
+ "role": "Auditor"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py
new file mode 100644
index 0000000..9e5e63e
--- /dev/null
+++ b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py
@@ -0,0 +1,188 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from collections import OrderedDict
+import datetime
+from typing import Dict, List, Tuple, Union
+
+import frappe
+from frappe import _
+from frappe.utils import date_diff
+
+from erpnext.accounts.report.general_ledger.general_ledger import get_gl_entries
+
+
+Filters = frappe._dict
+Row = frappe._dict
+Data = List[Row]
+Columns = List[Dict[str, str]]
+DateTime = Union[datetime.date, datetime.datetime]
+FilteredEntries = List[Dict[str, Union[str, float, DateTime, None]]]
+ItemGroupsDict = Dict[Tuple[int, int], Dict[str, Union[str, int]]]
+SVDList = List[frappe._dict]
+
+
+def execute(filters: Filters) -> Tuple[Columns, Data]:
+ update_filters_with_account(filters)
+ validate_filters(filters)
+ columns = get_columns()
+ data = get_data(filters)
+ return columns, data
+
+
+def update_filters_with_account(filters: Filters) -> None:
+ account = frappe.get_value("Company", filters.get("company"), "default_expense_account")
+ filters.update(dict(account=account))
+
+
+def validate_filters(filters: Filters) -> None:
+ if filters.from_date > filters.to_date:
+ frappe.throw(_("From Date must be before To Date"))
+
+
+def get_columns() -> Columns:
+ return [
+ {
+ 'label': 'Item Group',
+ 'fieldname': 'item_group',
+ 'fieldtype': 'Data',
+ 'width': '200'
+ },
+ {
+ 'label': 'COGS Debit',
+ 'fieldname': 'cogs_debit',
+ 'fieldtype': 'Currency',
+ 'width': '200'
+ }
+ ]
+
+
+def get_data(filters: Filters) -> Data:
+ filtered_entries = get_filtered_entries(filters)
+ svd_list = get_stock_value_difference_list(filtered_entries)
+ leveled_dict = get_leveled_dict()
+
+ assign_self_values(leveled_dict, svd_list)
+ assign_agg_values(leveled_dict)
+
+ data = []
+ for item in leveled_dict.items():
+ i = item[1]
+ if i['agg_value'] == 0:
+ continue
+ data.append(get_row(i['name'], i['agg_value'], i['is_group'], i['level']))
+ if i['self_value'] < i['agg_value'] and i['self_value'] > 0:
+ data.append(get_row(i['name'], i['self_value'], 0, i['level'] + 1))
+ return data
+
+
+def get_filtered_entries(filters: Filters) -> FilteredEntries:
+ gl_entries = get_gl_entries(filters, [])
+ filtered_entries = []
+ for entry in gl_entries:
+ posting_date = entry.get('posting_date')
+ from_date = filters.get('from_date')
+ if date_diff(from_date, posting_date) > 0:
+ continue
+ filtered_entries.append(entry)
+ return filtered_entries
+
+
+def get_stock_value_difference_list(filtered_entries: FilteredEntries) -> SVDList:
+ voucher_nos = [fe.get('voucher_no') for fe in filtered_entries]
+ svd_list = frappe.get_list(
+ 'Stock Ledger Entry', fields=['item_code','stock_value_difference'],
+ filters=[('voucher_no', 'in', voucher_nos)]
+ )
+ assign_item_groups_to_svd_list(svd_list)
+ return svd_list
+
+
+def get_leveled_dict() -> OrderedDict:
+ item_groups_dict = get_item_groups_dict()
+ lr_list = sorted(item_groups_dict, key=lambda x : int(x[0]))
+ leveled_dict = OrderedDict()
+ current_level = 0
+ nesting_r = []
+ for l, r in lr_list:
+ while current_level > 0 and nesting_r[-1] < l:
+ nesting_r.pop()
+ current_level -= 1
+
+ leveled_dict[(l,r)] = {
+ 'level' : current_level,
+ 'name' : item_groups_dict[(l,r)]['name'],
+ 'is_group' : item_groups_dict[(l,r)]['is_group']
+ }
+
+ if int(r) - int(l) > 1:
+ current_level += 1
+ nesting_r.append(r)
+
+ update_leveled_dict(leveled_dict)
+ return leveled_dict
+
+
+def assign_self_values(leveled_dict: OrderedDict, svd_list: SVDList) -> None:
+ key_dict = {v['name']:k for k, v in leveled_dict.items()}
+ for item in svd_list:
+ key = key_dict[item.get("item_group")]
+ leveled_dict[key]['self_value'] += -item.get("stock_value_difference")
+
+
+def assign_agg_values(leveled_dict: OrderedDict) -> None:
+ keys = list(leveled_dict.keys())[::-1]
+ prev_level = leveled_dict[keys[-1]]['level']
+ accu = [0]
+ for k in keys[:-1]:
+ curr_level = leveled_dict[k]['level']
+ if curr_level == prev_level:
+ accu[-1] += leveled_dict[k]['self_value']
+ leveled_dict[k]['agg_value'] = leveled_dict[k]['self_value']
+
+ elif curr_level > prev_level:
+ accu.append(leveled_dict[k]['self_value'])
+ leveled_dict[k]['agg_value'] = accu[-1]
+
+ elif curr_level < prev_level:
+ accu[-1] += leveled_dict[k]['self_value']
+ leveled_dict[k]['agg_value'] = accu[-1]
+
+ prev_level = curr_level
+
+ # root node
+ rk = keys[-1]
+ leveled_dict[rk]['agg_value'] = sum(accu) + leveled_dict[rk]['self_value']
+
+
+def get_row(name:str, value:float, is_bold:int, indent:int) -> Row:
+ item_group = name
+ if is_bold:
+ item_group = frappe.bold(item_group)
+ return frappe._dict(item_group=item_group, cogs_debit=value, indent=indent)
+
+
+def assign_item_groups_to_svd_list(svd_list: SVDList) -> None:
+ ig_map = get_item_groups_map(svd_list)
+ for item in svd_list:
+ item.item_group = ig_map[item.get("item_code")]
+
+
+def get_item_groups_map(svd_list: SVDList) -> Dict[str, str]:
+ item_codes = set(i['item_code'] for i in svd_list)
+ ig_list = frappe.get_list(
+ 'Item', fields=['item_code','item_group'],
+ filters=[('item_code', 'in', item_codes)]
+ )
+ return {i['item_code']:i['item_group'] for i in ig_list}
+
+
+def get_item_groups_dict() -> ItemGroupsDict:
+ item_groups_list = frappe.get_all("Item Group", fields=("name", "is_group", "lft", "rgt"))
+ return {(i['lft'],i['rgt']):{'name':i['name'], 'is_group':i['is_group']}
+ for i in item_groups_list}
+
+
+def update_leveled_dict(leveled_dict: OrderedDict) -> None:
+ for k in leveled_dict:
+ leveled_dict[k].update({'self_value':0, 'agg_value':0})
diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py
index 0cc8ca4..d446850 100644
--- a/erpnext/stock/report/stock_analytics/stock_analytics.py
+++ b/erpnext/stock/report/stock_analytics/stock_analytics.py
@@ -114,14 +114,41 @@
def get_periodic_data(entry, filters):
+ """Structured as:
+ Item 1
+ - Balance (updated and carried forward):
+ - Warehouse A : bal_qty/value
+ - Warehouse B : bal_qty/value
+ - Jun 2021 (sum of warehouse quantities used in report)
+ - Warehouse A : bal_qty/value
+ - Warehouse B : bal_qty/value
+ - Jul 2021 (sum of warehouse quantities used in report)
+ - Warehouse A : bal_qty/value
+ - Warehouse B : bal_qty/value
+ Item 2
+ - Balance (updated and carried forward):
+ - Warehouse A : bal_qty/value
+ - Warehouse B : bal_qty/value
+ - Jun 2021 (sum of warehouse quantities used in report)
+ - Warehouse A : bal_qty/value
+ - Warehouse B : bal_qty/value
+ - Jul 2021 (sum of warehouse quantities used in report)
+ - Warehouse A : bal_qty/value
+ - Warehouse B : bal_qty/value
+ """
periodic_data = {}
for d in entry:
period = get_period(d.posting_date, filters)
bal_qty = 0
+ # if period against item does not exist yet, instantiate it
+ # insert existing balance dict against period, and add/subtract to it
+ if periodic_data.get(d.item_code) and not periodic_data.get(d.item_code).get(period):
+ periodic_data[d.item_code][period] = periodic_data[d.item_code]['balance']
+
if d.voucher_type == "Stock Reconciliation":
- if periodic_data.get(d.item_code):
- bal_qty = periodic_data[d.item_code]["balance"]
+ if periodic_data.get(d.item_code) and periodic_data.get(d.item_code).get('balance').get(d.warehouse):
+ bal_qty = periodic_data[d.item_code]['balance'][d.warehouse]
qty_diff = d.qty_after_transaction - bal_qty
else:
@@ -132,12 +159,12 @@
else:
value = d.stock_value_difference
- periodic_data.setdefault(d.item_code, {}).setdefault(period, 0.0)
- periodic_data.setdefault(d.item_code, {}).setdefault("balance", 0.0)
+ # period-warehouse wise balance
+ periodic_data.setdefault(d.item_code, {}).setdefault('balance', {}).setdefault(d.warehouse, 0.0)
+ periodic_data.setdefault(d.item_code, {}).setdefault(period, {}).setdefault(d.warehouse, 0.0)
- periodic_data[d.item_code]["balance"] += value
- periodic_data[d.item_code][period] = periodic_data[d.item_code]["balance"]
-
+ periodic_data[d.item_code]['balance'][d.warehouse] += value
+ periodic_data[d.item_code][period][d.warehouse] = periodic_data[d.item_code]['balance'][d.warehouse]
return periodic_data
@@ -160,7 +187,8 @@
total = 0
for dummy, end_date in ranges:
period = get_period(end_date, filters)
- amount = flt(periodic_data.get(item_data.name, {}).get(period))
+ period_data = periodic_data.get(item_data.name, {}).get(period)
+ amount = sum(period_data.values()) if period_data else 0
row[scrub(period)] = amount
total += amount
row["total"] = total
diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
index 14d543b..bfc4471 100644
--- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
+++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
@@ -22,6 +22,7 @@
data = []
filters = {
+ "is_cancelled": 0,
"company": report_filters.company,
"posting_date": ("<=", report_filters.as_on_date)
}
@@ -34,7 +35,7 @@
key = (d.voucher_type, d.voucher_no)
gl_data = voucher_wise_gl_data.get(key) or {}
d.account_value = gl_data.get("account_value", 0)
- d.difference_value = (d.stock_value - d.account_value)
+ d.difference_value = abs(d.stock_value - d.account_value)
if abs(d.difference_value) > 0.1:
data.append(d)
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index b6a8063..fc3d719 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -16,8 +16,6 @@
is_reposting_item_valuation_in_progress()
if not filters: filters = {}
- validate_filters(filters)
-
from_date = filters.get('from_date')
to_date = filters.get('to_date')
@@ -237,12 +235,15 @@
return iwb_map
def get_items(filters):
+ "Get items based on item code, item group or brand."
conditions = []
if filters.get("item_code"):
conditions.append("item.name=%(item_code)s")
else:
if filters.get("item_group"):
conditions.append(get_item_group_condition(filters.get("item_group")))
+ if filters.get("brand"): # used in stock analytics report
+ conditions.append("item.brand=%(brand)s")
items = []
if conditions:
@@ -295,12 +296,6 @@
return dict((d.parent + d.warehouse, d) for d in item_reorder_details)
-def validate_filters(filters):
- if not (filters.get("item_code") or filters.get("warehouse")):
- sle_count = flt(frappe.db.sql("""select count(name) from `tabStock Ledger Entry`""")[0][0])
- if sle_count > 500000:
- frappe.throw(_("Please set filter based on Item or Warehouse due to a large amount of entries."))
-
def get_variants_attributes():
'''Return all item variant attributes.'''
return [i.name for i in frappe.get_all('Item Attribute')]
diff --git a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py
index 5873a7a..4108a57 100644
--- a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py
+++ b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py
@@ -69,7 +69,7 @@
i.stock_uom, sle.actual_qty, sle.stock_value_difference,
sle.voucher_no, sle.voucher_type
from `tabStock Ledger Entry` sle, `tabItem` i
- where sle.item_code=i.name and sle.actual_qty < 0 %s""" % conditions, values, as_dict=1):
+ where sle.is_cancelled = 0 and sle.item_code=i.name and sle.actual_qty < 0 %s""" % conditions, values, as_dict=1):
consumed_details.setdefault(d.item_code, []).append(d)
return consumed_details
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index c15d1ed..eddd048 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -127,30 +127,26 @@
sle.submit()
return sle
-def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negative_stock=None, via_landed_cost_voucher=False):
+def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negative_stock=None, via_landed_cost_voucher=False, doc=None):
if not args and voucher_type and voucher_no:
- args = get_args_for_voucher(voucher_type, voucher_no)
+ args = get_items_to_be_repost(voucher_type, voucher_no, doc)
- distinct_item_warehouses = {}
- for i, d in enumerate(args):
- distinct_item_warehouses.setdefault((d.item_code, d.warehouse), frappe._dict({
- "reposting_status": False,
- "sle": d,
- "args_idx": i
- }))
+ distinct_item_warehouses = get_distinct_item_warehouse(args, doc)
- i = 0
+ i = get_current_index(doc) or 0
while i < len(args):
+ validate_item_warehouse(args[i])
+
obj = update_entries_after({
- "item_code": args[i].item_code,
- "warehouse": args[i].warehouse,
- "posting_date": args[i].posting_date,
- "posting_time": args[i].posting_time,
- "creation": args[i].get("creation"),
- "distinct_item_warehouses": distinct_item_warehouses
+ 'item_code': args[i].get('item_code'),
+ 'warehouse': args[i].get('warehouse'),
+ 'posting_date': args[i].get('posting_date'),
+ 'posting_time': args[i].get('posting_time'),
+ 'creation': args[i].get('creation'),
+ 'distinct_item_warehouses': distinct_item_warehouses
}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
- distinct_item_warehouses[(args[i].item_code, args[i].warehouse)].reposting_status = True
+ distinct_item_warehouses[(args[i].get('item_code'), args[i].get('warehouse'))].reposting_status = True
if obj.new_items_found:
for item_wh, data in iteritems(distinct_item_warehouses):
@@ -159,11 +155,41 @@
args.append(data.sle)
elif data.sle_changed and not data.reposting_status:
args[data.args_idx] = data.sle
-
+
data.sle_changed = False
i += 1
-def get_args_for_voucher(voucher_type, voucher_no):
+ if doc and i % 2 == 0:
+ update_args_in_repost_item_valuation(doc, i, args, distinct_item_warehouses)
+
+ if doc and args:
+ update_args_in_repost_item_valuation(doc, i, args, distinct_item_warehouses)
+
+def validate_item_warehouse(args):
+ for field in ['item_code', 'warehouse', 'posting_date', 'posting_time']:
+ if not args.get(field):
+ validation_msg = f'The field {frappe.unscrub(args.get(field))} is required for the reposting'
+ frappe.throw(_(validation_msg))
+
+def update_args_in_repost_item_valuation(doc, index, args, distinct_item_warehouses):
+ frappe.db.set_value(doc.doctype, doc.name, {
+ 'items_to_be_repost': json.dumps(args, default=str),
+ 'distinct_item_and_warehouse': json.dumps({str(k): v for k,v in distinct_item_warehouses.items()}, default=str),
+ 'current_index': index
+ })
+
+ frappe.db.commit()
+
+ frappe.publish_realtime('item_reposting_progress', {
+ 'name': doc.name,
+ 'items_to_be_repost': json.dumps(args, default=str),
+ 'current_index': index
+ })
+
+def get_items_to_be_repost(voucher_type, voucher_no, doc=None):
+ if doc and doc.items_to_be_repost:
+ return json.loads(doc.items_to_be_repost) or []
+
return frappe.db.get_all("Stock Ledger Entry",
filters={"voucher_type": voucher_type, "voucher_no": voucher_no},
fields=["item_code", "warehouse", "posting_date", "posting_time", "creation"],
@@ -171,6 +197,25 @@
group_by="item_code, warehouse"
)
+def get_distinct_item_warehouse(args=None, doc=None):
+ distinct_item_warehouses = {}
+ if doc and doc.distinct_item_and_warehouse:
+ distinct_item_warehouses = json.loads(doc.distinct_item_and_warehouse)
+ distinct_item_warehouses = {frappe.safe_eval(k): frappe._dict(v) for k, v in distinct_item_warehouses.items()}
+ else:
+ for i, d in enumerate(args):
+ distinct_item_warehouses.setdefault((d.item_code, d.warehouse), frappe._dict({
+ "reposting_status": False,
+ "sle": d,
+ "args_idx": i
+ }))
+
+ return distinct_item_warehouses
+
+def get_current_index(doc=None):
+ if doc and doc.current_index:
+ return doc.current_index
+
class update_entries_after(object):
"""
update valution rate and qty after transaction
@@ -234,15 +279,13 @@
}
"""
- self.data.setdefault(args.warehouse, frappe._dict())
- warehouse_dict = self.data[args.warehouse]
previous_sle = get_previous_sle_of_current_voucher(args)
- warehouse_dict.previous_sle = previous_sle
- for key in ("qty_after_transaction", "valuation_rate", "stock_value"):
- setattr(warehouse_dict, key, flt(previous_sle.get(key)))
-
- warehouse_dict.update({
+ self.data[args.warehouse] = frappe._dict({
+ "previous_sle": previous_sle,
+ "qty_after_transaction": flt(previous_sle.qty_after_transaction),
+ "valuation_rate": flt(previous_sle.valuation_rate),
+ "stock_value": flt(previous_sle.stock_value),
"prev_stock_value": previous_sle.stock_value or 0.0,
"stock_queue": json.loads(previous_sle.stock_queue or "[]"),
"stock_value_difference": 0.0
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index b57b2aa..9f6d0a8 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -224,7 +224,7 @@
def get_valuation_method(item_code):
"""get valuation method from item or default"""
- val_method = frappe.db.get_value('Item', item_code, 'valuation_method')
+ val_method = frappe.db.get_value('Item', item_code, 'valuation_method', cache=True)
if not val_method:
val_method = frappe.db.get_value("Stock Settings", None, "valuation_method") or "FIFO"
return val_method
@@ -275,17 +275,17 @@
return valid_serial_nos
def validate_warehouse_company(warehouse, company):
- warehouse_company = frappe.db.get_value("Warehouse", warehouse, "company")
+ warehouse_company = frappe.db.get_value("Warehouse", warehouse, "company", cache=True)
if warehouse_company and warehouse_company != company:
frappe.throw(_("Warehouse {0} does not belong to company {1}").format(warehouse, company),
InvalidWarehouseCompany)
def is_group_warehouse(warehouse):
- if frappe.db.get_value("Warehouse", warehouse, "is_group"):
+ if frappe.db.get_value("Warehouse", warehouse, "is_group", cache=True):
frappe.throw(_("Group node warehouse is not allowed to select for transactions"))
def validate_disabled_warehouse(warehouse):
- if frappe.db.get_value("Warehouse", warehouse, "disabled"):
+ if frappe.db.get_value("Warehouse", warehouse, "disabled", cache=True):
frappe.throw(_("Disabled Warehouse {0} cannot be used for this transaction.").format(get_link_to_form('Warehouse', warehouse)))
def update_included_uom_in_report(columns, result, include_uom, conversion_factors):
diff --git a/erpnext/stock/workspace/stock/stock.json b/erpnext/stock/workspace/stock/stock.json
index 529ce8e..26d10ce 100644
--- a/erpnext/stock/workspace/stock/stock.json
+++ b/erpnext/stock/workspace/stock/stock.json
@@ -1,28 +1,32 @@
{
"cards_label": "Masters & Reports",
- "category": "Modules",
+ "category": "",
"charts": [
{
"chart_name": "Warehouse wise Stock Value"
}
],
+ "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Stock\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": null, \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Quick Access\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Material Request\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Receipt\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Delivery Note\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Ledger\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Balance\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Masters & Reports\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Items and Pricing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Stock Transactions\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Stock Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Serial No and Batch\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tools\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Incorrect Data Report\", \"col\": 4}}]",
"creation": "2020-03-02 15:43:10.096528",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "stock",
"idx": 0,
"is_default": 0,
- "is_standard": 1,
+ "is_standard": 0,
"label": "Stock",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Items and Pricing",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -31,6 +35,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item",
+ "link_count": 0,
"link_to": "Item",
"link_type": "DocType",
"onboard": 1,
@@ -41,6 +46,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item Group",
+ "link_count": 0,
"link_to": "Item Group",
"link_type": "DocType",
"onboard": 1,
@@ -51,6 +57,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Product Bundle",
+ "link_count": 0,
"link_to": "Product Bundle",
"link_type": "DocType",
"onboard": 1,
@@ -61,6 +68,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Price List",
+ "link_count": 0,
"link_to": "Price List",
"link_type": "DocType",
"onboard": 0,
@@ -71,6 +79,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item Price",
+ "link_count": 0,
"link_to": "Item Price",
"link_type": "DocType",
"onboard": 0,
@@ -81,6 +90,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Shipping Rule",
+ "link_count": 0,
"link_to": "Shipping Rule",
"link_type": "DocType",
"onboard": 0,
@@ -91,6 +101,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Pricing Rule",
+ "link_count": 0,
"link_to": "Pricing Rule",
"link_type": "DocType",
"onboard": 0,
@@ -101,6 +112,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item Alternative",
+ "link_count": 0,
"link_to": "Item Alternative",
"link_type": "DocType",
"onboard": 0,
@@ -111,6 +123,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item Manufacturer",
+ "link_count": 0,
"link_to": "Item Manufacturer",
"link_type": "DocType",
"onboard": 0,
@@ -121,6 +134,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Customs Tariff Number",
+ "link_count": 0,
"link_to": "Customs Tariff Number",
"link_type": "DocType",
"onboard": 0,
@@ -130,6 +144,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Stock Transactions",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -138,6 +153,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Material Request",
+ "link_count": 0,
"link_to": "Material Request",
"link_type": "DocType",
"onboard": 1,
@@ -148,6 +164,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Stock Entry",
+ "link_count": 0,
"link_to": "Stock Entry",
"link_type": "DocType",
"onboard": 1,
@@ -158,6 +175,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Delivery Note",
+ "link_count": 0,
"link_to": "Delivery Note",
"link_type": "DocType",
"onboard": 1,
@@ -168,6 +186,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Purchase Receipt",
+ "link_count": 0,
"link_to": "Purchase Receipt",
"link_type": "DocType",
"onboard": 1,
@@ -178,6 +197,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Pick List",
+ "link_count": 0,
"link_to": "Pick List",
"link_type": "DocType",
"onboard": 1,
@@ -188,6 +208,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Delivery Trip",
+ "link_count": 0,
"link_to": "Delivery Trip",
"link_type": "DocType",
"onboard": 0,
@@ -197,6 +218,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Stock Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -205,6 +227,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Stock Ledger",
+ "link_count": 0,
"link_to": "Stock Ledger",
"link_type": "Report",
"onboard": 1,
@@ -215,6 +238,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Stock Balance",
+ "link_count": 0,
"link_to": "Stock Balance",
"link_type": "Report",
"onboard": 1,
@@ -225,6 +249,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Stock Projected Qty",
+ "link_count": 0,
"link_to": "Stock Projected Qty",
"link_type": "Report",
"onboard": 1,
@@ -235,6 +260,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Stock Summary",
+ "link_count": 0,
"link_to": "stock-balance",
"link_type": "Page",
"onboard": 0,
@@ -245,6 +271,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Stock Ageing",
+ "link_count": 0,
"link_to": "Stock Ageing",
"link_type": "Report",
"onboard": 0,
@@ -255,6 +282,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Item Price Stock",
+ "link_count": 0,
"link_to": "Item Price Stock",
"link_type": "Report",
"onboard": 0,
@@ -264,6 +292,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Settings",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -272,6 +301,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Stock Settings",
+ "link_count": 0,
"link_to": "Stock Settings",
"link_type": "DocType",
"onboard": 1,
@@ -282,6 +312,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Warehouse",
+ "link_count": 0,
"link_to": "Warehouse",
"link_type": "DocType",
"onboard": 1,
@@ -292,6 +323,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Unit of Measure (UOM)",
+ "link_count": 0,
"link_to": "UOM",
"link_type": "DocType",
"onboard": 1,
@@ -302,6 +334,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item Variant Settings",
+ "link_count": 0,
"link_to": "Item Variant Settings",
"link_type": "DocType",
"onboard": 1,
@@ -312,6 +345,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Brand",
+ "link_count": 0,
"link_to": "Brand",
"link_type": "DocType",
"onboard": 1,
@@ -322,6 +356,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item Attribute",
+ "link_count": 0,
"link_to": "Item Attribute",
"link_type": "DocType",
"onboard": 0,
@@ -332,6 +367,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "UOM Conversion Factor",
+ "link_count": 0,
"link_to": "UOM Conversion Factor",
"link_type": "DocType",
"onboard": 0,
@@ -341,6 +377,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Serial No and Batch",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -349,6 +386,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Serial No",
+ "link_count": 0,
"link_to": "Serial No",
"link_type": "DocType",
"onboard": 1,
@@ -359,6 +397,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Batch",
+ "link_count": 0,
"link_to": "Batch",
"link_type": "DocType",
"onboard": 1,
@@ -369,6 +408,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Installation Note",
+ "link_count": 0,
"link_to": "Installation Note",
"link_type": "DocType",
"onboard": 0,
@@ -379,6 +419,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Serial No Service Contract Expiry",
+ "link_count": 0,
"link_to": "Serial No Service Contract Expiry",
"link_type": "Report",
"onboard": 0,
@@ -389,6 +430,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Serial No Status",
+ "link_count": 0,
"link_to": "Serial No Status",
"link_type": "Report",
"onboard": 0,
@@ -399,6 +441,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Serial No Warranty Expiry",
+ "link_count": 0,
"link_to": "Serial No Warranty Expiry",
"link_type": "Report",
"onboard": 0,
@@ -408,6 +451,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Tools",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -416,6 +460,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Stock Reconciliation",
+ "link_count": 0,
"link_to": "Stock Reconciliation",
"link_type": "DocType",
"onboard": 1,
@@ -426,6 +471,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Landed Cost Voucher",
+ "link_count": 0,
"link_to": "Landed Cost Voucher",
"link_type": "DocType",
"onboard": 1,
@@ -436,6 +482,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Packing Slip",
+ "link_count": 0,
"link_to": "Packing Slip",
"link_type": "DocType",
"onboard": 1,
@@ -446,6 +493,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Quality Inspection",
+ "link_count": 0,
"link_to": "Quality Inspection",
"link_type": "DocType",
"onboard": 0,
@@ -456,6 +504,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Quality Inspection Template",
+ "link_count": 0,
"link_to": "Quality Inspection Template",
"link_type": "DocType",
"onboard": 0,
@@ -466,6 +515,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Quick Stock Balance",
+ "link_count": 0,
"link_to": "Quick Stock Balance",
"link_type": "DocType",
"onboard": 0,
@@ -475,6 +525,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Key Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -483,6 +534,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Item-wise Price List Rate",
+ "link_count": 0,
"link_to": "Item-wise Price List Rate",
"link_type": "Report",
"onboard": 1,
@@ -493,6 +545,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Stock Analytics",
+ "link_count": 0,
"link_to": "Stock Analytics",
"link_type": "Report",
"onboard": 1,
@@ -503,6 +556,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Stock Qty vs Serial No Count",
+ "link_count": 0,
"link_to": "Stock Qty vs Serial No Count",
"link_type": "Report",
"onboard": 1,
@@ -513,6 +567,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Delivery Note Trends",
+ "link_count": 0,
"link_to": "Delivery Note Trends",
"link_type": "Report",
"onboard": 0,
@@ -523,6 +578,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Purchase Receipt Trends",
+ "link_count": 0,
"link_to": "Purchase Receipt Trends",
"link_type": "Report",
"onboard": 0,
@@ -533,6 +589,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Sales Order Analysis",
+ "link_count": 0,
"link_to": "Sales Order Analysis",
"link_type": "Report",
"onboard": 0,
@@ -543,6 +600,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Purchase Order Analysis",
+ "link_count": 0,
"link_to": "Purchase Order Analysis",
"link_type": "Report",
"onboard": 0,
@@ -553,6 +611,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Item Shortage Report",
+ "link_count": 0,
"link_to": "Item Shortage Report",
"link_type": "Report",
"onboard": 0,
@@ -563,6 +622,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Batch-Wise Balance History",
+ "link_count": 0,
"link_to": "Batch-Wise Balance History",
"link_type": "Report",
"onboard": 0,
@@ -572,6 +632,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Other Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -580,6 +641,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Requested Items To Be Transferred",
+ "link_count": 0,
"link_to": "Requested Items To Be Transferred",
"link_type": "Report",
"onboard": 0,
@@ -590,6 +652,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Batch Item Expiry Status",
+ "link_count": 0,
"link_to": "Batch Item Expiry Status",
"link_type": "Report",
"onboard": 0,
@@ -600,6 +663,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Item Prices",
+ "link_count": 0,
"link_to": "Item Prices",
"link_type": "Report",
"onboard": 0,
@@ -610,6 +674,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Itemwise Recommended Reorder Level",
+ "link_count": 0,
"link_to": "Itemwise Recommended Reorder Level",
"link_type": "Report",
"onboard": 0,
@@ -620,6 +685,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Item Variant Details",
+ "link_count": 0,
"link_to": "Item Variant Details",
"link_type": "Report",
"onboard": 0,
@@ -630,6 +696,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Subcontracted Raw Materials To Be Transferred",
+ "link_count": 0,
"link_to": "Subcontracted Raw Materials To Be Transferred",
"link_type": "Report",
"onboard": 0,
@@ -640,6 +707,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Subcontracted Item To Be Received",
+ "link_count": 0,
"link_to": "Subcontracted Item To Be Received",
"link_type": "Report",
"onboard": 0,
@@ -650,6 +718,7 @@
"hidden": 0,
"is_query_report": 1,
"label": "Stock and Account Value Comparison",
+ "link_count": 0,
"link_to": "Stock and Account Value Comparison",
"link_type": "Report",
"onboard": 0,
@@ -659,6 +728,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Incorrect Data Report",
+ "link_count": 0,
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
@@ -667,6 +737,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Incorrect Serial No Qty and Valuation",
+ "link_count": 0,
"link_to": "Incorrect Serial No Valuation",
"link_type": "Report",
"onboard": 0,
@@ -676,6 +747,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Incorrect Balance Qty After Transaction",
+ "link_count": 0,
"link_to": "Incorrect Balance Qty After Transaction",
"link_type": "Report",
"onboard": 0,
@@ -685,20 +757,26 @@
"hidden": 0,
"is_query_report": 0,
"label": "Stock and Account Value Comparison",
+ "link_count": 0,
"link_to": "Stock and Account Value Comparison",
"link_type": "Report",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2021-05-13 13:10:24.914983",
+ "modified": "2021-08-05 12:16:02.361509",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock",
"onboarding": "Stock",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 24,
"shortcuts": [
{
"color": "Green",
@@ -753,5 +831,6 @@
"type": "Dashboard"
}
],
- "shortcuts_label": "Quick Access"
+ "shortcuts_label": "Quick Access",
+ "title": "Stock"
}
\ No newline at end of file
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index b9a65b6..24dadd5 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -116,6 +116,10 @@
}).insert(ignore_permissions=True)
return replicated_issue.name
+
+ def reset_issue_metrics(self):
+ self.db_set("resolution_time", None)
+ self.db_set("user_resolution_time", None)
def get_list_context(context=None):
return {
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
index 8739cb2..cfa264f 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
@@ -281,15 +281,18 @@
def get_documents_with_active_service_level_agreement():
- if not frappe.cache().hget("service_level_agreement", "active"):
- set_documents_with_active_service_level_agreement()
+ sla_doctypes = frappe.cache().hget("service_level_agreement", "active")
- return frappe.cache().hget("service_level_agreement", "active")
+ if sla_doctypes is None:
+ return set_documents_with_active_service_level_agreement()
+
+ return sla_doctypes
def set_documents_with_active_service_level_agreement():
active = [sla.document_type for sla in frappe.get_all("Service Level Agreement", fields=["document_type"])]
frappe.cache().hset("service_level_agreement", "active", active)
+ return active
def apply(doc, method=None):
diff --git a/erpnext/support/workspace/support/support.json b/erpnext/support/workspace/support/support.json
index 01a8676..4c5829d 100644
--- a/erpnext/support/workspace/support/support.json
+++ b/erpnext/support/workspace/support/support.json
@@ -1,22 +1,27 @@
{
- "category": "Modules",
+ "category": "",
"charts": [],
+ "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Issue\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Maintenance Visit\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Service Level Agreement\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Issues\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Maintenance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Service Level Agreement\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Warranty\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
"creation": "2020-03-02 15:48:23.224699",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"icon": "support",
"idx": 0,
- "is_standard": 1,
+ "is_default": 0,
+ "is_standard": 0,
"label": "Support",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Issues",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -25,6 +30,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Issue",
+ "link_count": 0,
"link_to": "Issue",
"link_type": "DocType",
"onboard": 1,
@@ -35,6 +41,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Issue Type",
+ "link_count": 0,
"link_to": "Issue Type",
"link_type": "DocType",
"onboard": 0,
@@ -45,6 +52,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Issue Priority",
+ "link_count": 0,
"link_to": "Issue Priority",
"link_type": "DocType",
"onboard": 0,
@@ -54,6 +62,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Maintenance",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -62,6 +71,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Maintenance Schedule",
+ "link_count": 0,
"link_to": "Maintenance Schedule",
"link_type": "DocType",
"onboard": 0,
@@ -72,6 +82,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Maintenance Visit",
+ "link_count": 0,
"link_to": "Maintenance Visit",
"link_type": "DocType",
"onboard": 0,
@@ -81,6 +92,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Service Level Agreement",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -89,6 +101,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Service Level Agreement",
+ "link_count": 0,
"link_to": "Service Level Agreement",
"link_type": "DocType",
"onboard": 0,
@@ -98,6 +111,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Warranty",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -106,6 +120,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Warranty Claim",
+ "link_count": 0,
"link_to": "Warranty Claim",
"link_type": "DocType",
"onboard": 0,
@@ -116,6 +131,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Serial No",
+ "link_count": 0,
"link_to": "Serial No",
"link_type": "DocType",
"onboard": 0,
@@ -125,6 +141,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Settings",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -133,6 +150,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Support Settings",
+ "link_count": 0,
"link_to": "Support Settings",
"link_type": "DocType",
"onboard": 0,
@@ -142,6 +160,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Reports",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -150,19 +169,26 @@
"hidden": 0,
"is_query_report": 1,
"label": "First Response Time for Issues",
+ "link_count": 0,
"link_to": "First Response Time for Issues",
"link_type": "Report",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2020-12-01 13:38:37.073482",
+ "modified": "2021-08-05 12:16:02.699923",
"modified_by": "Administrator",
"module": "Support",
"name": "Support",
+ "onboarding": "",
"owner": "Administrator",
+ "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0,
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 25,
"shortcuts": [
{
"color": "Yellow",
@@ -182,5 +208,6 @@
"link_to": "Service Level Agreement",
"type": "DocType"
}
- ]
+ ],
+ "title": "Support"
}
\ No newline at end of file
diff --git a/erpnext/utilities/workspace/utilities/utilities.json b/erpnext/utilities/workspace/utilities/utilities.json
index 2f9250e..4ad4afb 100644
--- a/erpnext/utilities/workspace/utilities/utilities.json
+++ b/erpnext/utilities/workspace/utilities/utilities.json
@@ -1,21 +1,26 @@
{
- "category": "Modules",
+ "category": "",
"charts": [],
+ "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Video\", \"col\": 4}}]",
"creation": "2020-09-10 12:21:22.335307",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Workspace",
+ "extends": "",
"extends_another_page": 0,
+ "for_user": "",
"hide_custom": 0,
"idx": 0,
- "is_standard": 1,
+ "is_default": 0,
+ "is_standard": 0,
"label": "Utilities",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Video",
+ "link_count": 0,
"onboard": 0,
"type": "Card Break"
},
@@ -24,6 +29,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Video",
+ "link_count": 0,
"link_to": "Video",
"link_type": "DocType",
"onboard": 0,
@@ -34,18 +40,26 @@
"hidden": 0,
"is_query_report": 0,
"label": "Video Settings",
+ "link_count": 0,
"link_to": "Video Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2020-12-01 13:38:36.711884",
+ "modified": "2021-08-05 12:16:03.350804",
"modified_by": "Administrator",
"module": "Utilities",
"name": "Utilities",
+ "onboarding": "",
"owner": "user@erpnext.com",
- "pin_to_bottom": 1,
+ "parent_page": "",
+ "pin_to_bottom": 0,
"pin_to_top": 0,
- "shortcuts": []
+ "public": 1,
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 30,
+ "shortcuts": [],
+ "title": "Utilities"
}
\ No newline at end of file