Merge branch 'develop' into auto-attendance-issue
This commit is contained in:
commit
00f7f97dc9
7
.github/helper/.flake8_strict
vendored
7
.github/helper/.flake8_strict
vendored
@ -1,6 +1,8 @@
|
|||||||
[flake8]
|
[flake8]
|
||||||
ignore =
|
ignore =
|
||||||
B007,
|
B007,
|
||||||
|
B009,
|
||||||
|
B010,
|
||||||
B950,
|
B950,
|
||||||
E101,
|
E101,
|
||||||
E111,
|
E111,
|
||||||
@ -65,11 +67,6 @@ ignore =
|
|||||||
E713,
|
E713,
|
||||||
E712,
|
E712,
|
||||||
|
|
||||||
enable-extensions =
|
|
||||||
M90
|
|
||||||
|
|
||||||
select =
|
|
||||||
M511
|
|
||||||
|
|
||||||
max-line-length = 200
|
max-line-length = 200
|
||||||
exclude=.github/helper/semgrep_rules,test_*.py
|
exclude=.github/helper/semgrep_rules,test_*.py
|
||||||
|
@ -131,3 +131,21 @@ rules:
|
|||||||
key `$X` is uselessly assigned twice. This could be a potential bug.
|
key `$X` is uselessly assigned twice. This could be a potential bug.
|
||||||
languages: [python]
|
languages: [python]
|
||||||
severity: ERROR
|
severity: ERROR
|
||||||
|
|
||||||
|
|
||||||
|
- id: frappe-manual-commit
|
||||||
|
patterns:
|
||||||
|
- pattern: frappe.db.commit()
|
||||||
|
- pattern-not-inside: |
|
||||||
|
try:
|
||||||
|
...
|
||||||
|
except ...:
|
||||||
|
...
|
||||||
|
message: |
|
||||||
|
Manually commiting a transaction is highly discouraged. Read about the transaction model implemented by Frappe Framework before adding manual commits: https://frappeframework.com/docs/user/en/api/database#database-transaction-model If you think manual commit is required then add a comment explaining why and `// nosemgrep` on the same line.
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- "**/patches/**"
|
||||||
|
- "**/demo/**"
|
||||||
|
languages: [python]
|
||||||
|
severity: ERROR
|
||||||
|
@ -21,9 +21,9 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
additional_dependencies: [
|
additional_dependencies: [
|
||||||
'flake8-mutable',
|
'flake8-bugbear',
|
||||||
]
|
]
|
||||||
args: ['--select=M511', '--config', '.github/helper/.flake8_strict']
|
args: ['--config', '.github/helper/.flake8_strict']
|
||||||
exclude: ".*setup.py$"
|
exclude: ".*setup.py$"
|
||||||
|
|
||||||
- repo: https://github.com/timothycrosley/isort
|
- repo: https://github.com/timothycrosley/isort
|
||||||
|
@ -8,6 +8,8 @@ from frappe import _, throw
|
|||||||
from frappe.utils import cint, cstr
|
from frappe.utils import cint, cstr
|
||||||
from frappe.utils.nestedset import NestedSet, get_ancestors_of, get_descendants_of
|
from frappe.utils.nestedset import NestedSet, get_ancestors_of, get_descendants_of
|
||||||
|
|
||||||
|
import erpnext
|
||||||
|
|
||||||
|
|
||||||
class RootNotEditable(frappe.ValidationError): pass
|
class RootNotEditable(frappe.ValidationError): pass
|
||||||
class BalanceMismatchError(frappe.ValidationError): pass
|
class BalanceMismatchError(frappe.ValidationError): pass
|
||||||
@ -196,7 +198,7 @@ class Account(NestedSet):
|
|||||||
"company": company,
|
"company": company,
|
||||||
# parent account's currency should be passed down to child account's curreny
|
# parent account's currency should be passed down to child account's curreny
|
||||||
# if it is None, it picks it up from default company currency, which might be unintended
|
# if it is None, it picks it up from default company currency, which might be unintended
|
||||||
"account_currency": self.account_currency,
|
"account_currency": erpnext.get_company_currency(company),
|
||||||
"parent_account": parent_acc_name_map[company]
|
"parent_account": parent_acc_name_map[company]
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -207,8 +209,7 @@ class Account(NestedSet):
|
|||||||
# update the parent company's value in child companies
|
# update the parent company's value in child companies
|
||||||
doc = frappe.get_doc("Account", child_account)
|
doc = frappe.get_doc("Account", child_account)
|
||||||
parent_value_changed = False
|
parent_value_changed = False
|
||||||
for field in ['account_type', 'account_currency',
|
for field in ['account_type', 'freeze_account', 'balance_must_be']:
|
||||||
'freeze_account', 'balance_must_be']:
|
|
||||||
if doc.get(field) != self.get(field):
|
if doc.get(field) != self.get(field):
|
||||||
parent_value_changed = True
|
parent_value_changed = True
|
||||||
doc.set(field, self.get(field))
|
doc.set(field, self.get(field))
|
||||||
|
@ -174,7 +174,7 @@
|
|||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "automatically_fetch_payment_terms",
|
"fieldname": "automatically_fetch_payment_terms",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Automatically Fetch Payment Terms"
|
"label": "Automatically Fetch Payment Terms from Order"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "The percentage you are allowed to bill more against the amount ordered. For example, if the order value is $100 for an item and tolerance is set as 10%, then you are allowed to bill up to $110 ",
|
"description": "The percentage you are allowed to bill more against the amount ordered. For example, if the order value is $100 for an item and tolerance is set as 10%, then you are allowed to bill up to $110 ",
|
||||||
@ -282,7 +282,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-08-19 11:17:38.788054",
|
"modified": "2021-10-11 17:42:36.427699",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounts Settings",
|
"name": "Accounts Settings",
|
||||||
|
@ -16,7 +16,7 @@ class LoyaltyPointEntry(Document):
|
|||||||
|
|
||||||
def get_loyalty_point_entries(customer, loyalty_program, company, expiry_date=None):
|
def get_loyalty_point_entries(customer, loyalty_program, company, expiry_date=None):
|
||||||
if not expiry_date:
|
if not expiry_date:
|
||||||
date = today()
|
expiry_date = today()
|
||||||
|
|
||||||
return frappe.db.sql('''
|
return frappe.db.sql('''
|
||||||
select name, loyalty_points, expiry_date, loyalty_program_tier, invoice_type, invoice
|
select name, loyalty_points, expiry_date, loyalty_program_tier, invoice_type, invoice
|
||||||
|
@ -505,12 +505,13 @@ class PaymentEntry(AccountsController):
|
|||||||
|
|
||||||
def validate_received_amount(self):
|
def validate_received_amount(self):
|
||||||
if self.paid_from_account_currency == self.paid_to_account_currency:
|
if self.paid_from_account_currency == self.paid_to_account_currency:
|
||||||
if self.paid_amount != self.received_amount:
|
if self.paid_amount < self.received_amount:
|
||||||
frappe.throw(_("Received Amount cannot be greater than Paid Amount"))
|
frappe.throw(_("Received Amount cannot be greater than Paid Amount"))
|
||||||
|
|
||||||
def set_received_amount(self):
|
def set_received_amount(self):
|
||||||
self.base_received_amount = self.base_paid_amount
|
self.base_received_amount = self.base_paid_amount
|
||||||
if self.paid_from_account_currency == self.paid_to_account_currency:
|
if self.paid_from_account_currency == self.paid_to_account_currency \
|
||||||
|
and not self.payment_type == 'Internal Transfer':
|
||||||
self.received_amount = self.paid_amount
|
self.received_amount = self.paid_amount
|
||||||
|
|
||||||
def set_amounts_after_tax(self):
|
def set_amounts_after_tax(self):
|
||||||
@ -712,10 +713,14 @@ class PaymentEntry(AccountsController):
|
|||||||
dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
|
dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
|
||||||
|
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
|
cost_center = self.cost_center
|
||||||
|
if d.reference_doctype == "Sales Invoice" and not cost_center:
|
||||||
|
cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center")
|
||||||
gle = party_gl_dict.copy()
|
gle = party_gl_dict.copy()
|
||||||
gle.update({
|
gle.update({
|
||||||
"against_voucher_type": d.reference_doctype,
|
"against_voucher_type": d.reference_doctype,
|
||||||
"against_voucher": d.reference_name
|
"against_voucher": d.reference_name,
|
||||||
|
"cost_center": cost_center
|
||||||
})
|
})
|
||||||
|
|
||||||
allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate),
|
allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate),
|
||||||
|
@ -149,16 +149,18 @@
|
|||||||
"cb_17",
|
"cb_17",
|
||||||
"hold_comment",
|
"hold_comment",
|
||||||
"more_info",
|
"more_info",
|
||||||
|
"status",
|
||||||
|
"inter_company_invoice_reference",
|
||||||
|
"represents_company",
|
||||||
|
"column_break_147",
|
||||||
|
"is_internal_supplier",
|
||||||
|
"accounting_details_section",
|
||||||
"credit_to",
|
"credit_to",
|
||||||
"party_account_currency",
|
"party_account_currency",
|
||||||
"is_opening",
|
"is_opening",
|
||||||
"against_expense_account",
|
"against_expense_account",
|
||||||
"column_break_63",
|
"column_break_63",
|
||||||
"unrealized_profit_loss_account",
|
"unrealized_profit_loss_account",
|
||||||
"status",
|
|
||||||
"inter_company_invoice_reference",
|
|
||||||
"is_internal_supplier",
|
|
||||||
"represents_company",
|
|
||||||
"remarks",
|
"remarks",
|
||||||
"subscription_section",
|
"subscription_section",
|
||||||
"from_date",
|
"from_date",
|
||||||
@ -1171,6 +1173,15 @@
|
|||||||
"options": "fa fa-file-text",
|
"options": "fa fa-file-text",
|
||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fetch_from": "supplier.is_internal_supplier",
|
||||||
|
"fieldname": "is_internal_supplier",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
|
"label": "Is Internal Supplier",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "credit_to",
|
"fieldname": "credit_to",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@ -1196,7 +1207,7 @@
|
|||||||
"default": "No",
|
"default": "No",
|
||||||
"fieldname": "is_opening",
|
"fieldname": "is_opening",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Is Opening",
|
"label": "Is Opening Entry",
|
||||||
"oldfieldname": "is_opening",
|
"oldfieldname": "is_opening",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "No\nYes",
|
"options": "No\nYes",
|
||||||
@ -1298,15 +1309,6 @@
|
|||||||
"fieldname": "dimension_col_break",
|
"fieldname": "dimension_col_break",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"default": "0",
|
|
||||||
"fetch_from": "supplier.is_internal_supplier",
|
|
||||||
"fieldname": "is_internal_supplier",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"ignore_user_permissions": 1,
|
|
||||||
"label": "Is Internal Supplier",
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "tax_withholding_category",
|
"fieldname": "tax_withholding_category",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@ -1395,13 +1397,24 @@
|
|||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "Ignore Default Payment Terms Template",
|
"label": "Ignore Default Payment Terms Template",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"fieldname": "accounting_details_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Accounting Details",
|
||||||
|
"print_hide": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_147",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 204,
|
"idx": 204,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-09-28 13:10:28.351810",
|
"modified": "2021-10-12 20:55:16.145651",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice",
|
"name": "Purchase Invoice",
|
||||||
|
@ -124,6 +124,13 @@
|
|||||||
"total_advance",
|
"total_advance",
|
||||||
"outstanding_amount",
|
"outstanding_amount",
|
||||||
"disable_rounded_total",
|
"disable_rounded_total",
|
||||||
|
"column_break4",
|
||||||
|
"write_off_amount",
|
||||||
|
"base_write_off_amount",
|
||||||
|
"write_off_outstanding_amount_automatically",
|
||||||
|
"column_break_74",
|
||||||
|
"write_off_account",
|
||||||
|
"write_off_cost_center",
|
||||||
"advances_section",
|
"advances_section",
|
||||||
"allocate_advances_automatically",
|
"allocate_advances_automatically",
|
||||||
"get_advances",
|
"get_advances",
|
||||||
@ -144,13 +151,6 @@
|
|||||||
"column_break_90",
|
"column_break_90",
|
||||||
"change_amount",
|
"change_amount",
|
||||||
"account_for_change_amount",
|
"account_for_change_amount",
|
||||||
"column_break4",
|
|
||||||
"write_off_amount",
|
|
||||||
"base_write_off_amount",
|
|
||||||
"write_off_outstanding_amount_automatically",
|
|
||||||
"column_break_74",
|
|
||||||
"write_off_account",
|
|
||||||
"write_off_cost_center",
|
|
||||||
"terms_section_break",
|
"terms_section_break",
|
||||||
"tc_name",
|
"tc_name",
|
||||||
"terms",
|
"terms",
|
||||||
@ -161,14 +161,14 @@
|
|||||||
"column_break_84",
|
"column_break_84",
|
||||||
"language",
|
"language",
|
||||||
"more_information",
|
"more_information",
|
||||||
|
"status",
|
||||||
"inter_company_invoice_reference",
|
"inter_company_invoice_reference",
|
||||||
"is_internal_customer",
|
|
||||||
"represents_company",
|
"represents_company",
|
||||||
"customer_group",
|
"customer_group",
|
||||||
"campaign",
|
"campaign",
|
||||||
"is_discounted",
|
|
||||||
"col_break23",
|
"col_break23",
|
||||||
"status",
|
"is_internal_customer",
|
||||||
|
"is_discounted",
|
||||||
"source",
|
"source",
|
||||||
"more_info",
|
"more_info",
|
||||||
"debit_to",
|
"debit_to",
|
||||||
@ -2031,7 +2031,7 @@
|
|||||||
"link_fieldname": "consolidated_invoice"
|
"link_fieldname": "consolidated_invoice"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2021-10-02 03:36:10.251715",
|
"modified": "2021-10-11 20:19:38.667508",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
@ -1087,8 +1087,6 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
actual_qty_1 = get_qty_after_transaction(item_code = "_Test Item", warehouse = "Stores - TCP1")
|
actual_qty_1 = get_qty_after_transaction(item_code = "_Test Item", warehouse = "Stores - TCP1")
|
||||||
|
|
||||||
frappe.db.commit()
|
|
||||||
|
|
||||||
self.assertEqual(actual_qty_0 - 5, actual_qty_1)
|
self.assertEqual(actual_qty_0 - 5, actual_qty_1)
|
||||||
|
|
||||||
# outgoing_rate
|
# outgoing_rate
|
||||||
|
@ -103,8 +103,11 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
column.is_tree = true;
|
column.is_tree = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = default_formatter(value, row, column, data);
|
if (data && data.account && column.apply_currency_formatter) {
|
||||||
|
data.currency = erpnext.get_currency(column.company_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = default_formatter(value, row, column, data);
|
||||||
if (!data.parent_account) {
|
if (!data.parent_account) {
|
||||||
value = $(`<span>${value}</span>`);
|
value = $(`<span>${value}</span>`);
|
||||||
|
|
||||||
|
@ -3,12 +3,14 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import cint, flt, getdate
|
from frappe.utils import cint, flt, getdate
|
||||||
|
|
||||||
|
import erpnext
|
||||||
from erpnext.accounts.report.balance_sheet.balance_sheet import (
|
from erpnext.accounts.report.balance_sheet.balance_sheet import (
|
||||||
check_opening_balance,
|
|
||||||
get_chart_data,
|
get_chart_data,
|
||||||
get_provisional_profit_loss,
|
get_provisional_profit_loss,
|
||||||
)
|
)
|
||||||
@ -31,7 +33,7 @@ from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement
|
|||||||
from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (
|
from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (
|
||||||
get_report_summary as get_pl_summary,
|
get_report_summary as get_pl_summary,
|
||||||
)
|
)
|
||||||
from erpnext.accounts.report.utils import convert_to_presentation_currency
|
from erpnext.accounts.report.utils import convert, convert_to_presentation_currency
|
||||||
|
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
@ -42,7 +44,7 @@ def execute(filters=None):
|
|||||||
|
|
||||||
fiscal_year = get_fiscal_year_data(filters.get('from_fiscal_year'), filters.get('to_fiscal_year'))
|
fiscal_year = get_fiscal_year_data(filters.get('from_fiscal_year'), filters.get('to_fiscal_year'))
|
||||||
companies_column, companies = get_companies(filters)
|
companies_column, companies = get_companies(filters)
|
||||||
columns = get_columns(companies_column)
|
columns = get_columns(companies_column, filters)
|
||||||
|
|
||||||
if filters.get('report') == "Balance Sheet":
|
if filters.get('report') == "Balance Sheet":
|
||||||
data, message, chart, report_summary = get_balance_sheet_data(fiscal_year, companies, columns, filters)
|
data, message, chart, report_summary = get_balance_sheet_data(fiscal_year, companies, columns, filters)
|
||||||
@ -73,21 +75,24 @@ def get_balance_sheet_data(fiscal_year, companies, columns, filters):
|
|||||||
provisional_profit_loss, total_credit = get_provisional_profit_loss(asset, liability, equity,
|
provisional_profit_loss, total_credit = get_provisional_profit_loss(asset, liability, equity,
|
||||||
companies, filters.get('company'), company_currency, True)
|
companies, filters.get('company'), company_currency, True)
|
||||||
|
|
||||||
message, opening_balance = check_opening_balance(asset, liability, equity)
|
message, opening_balance = prepare_companywise_opening_balance(asset, liability, equity, companies)
|
||||||
|
|
||||||
if opening_balance and round(opening_balance,2) !=0:
|
if opening_balance:
|
||||||
unclosed ={
|
unclosed = {
|
||||||
"account_name": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
|
"account_name": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
|
||||||
"account": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
|
"account": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
|
||||||
"warn_if_negative": True,
|
"warn_if_negative": True,
|
||||||
"currency": company_currency
|
"currency": company_currency
|
||||||
}
|
}
|
||||||
for company in companies:
|
|
||||||
unclosed[company] = opening_balance
|
|
||||||
if provisional_profit_loss:
|
|
||||||
provisional_profit_loss[company] = provisional_profit_loss[company] - opening_balance
|
|
||||||
|
|
||||||
unclosed["total"]=opening_balance
|
for company in companies:
|
||||||
|
unclosed[company] = opening_balance.get(company)
|
||||||
|
if provisional_profit_loss and provisional_profit_loss.get(company):
|
||||||
|
provisional_profit_loss[company] = (
|
||||||
|
flt(provisional_profit_loss[company]) - flt(opening_balance.get(company))
|
||||||
|
)
|
||||||
|
|
||||||
|
unclosed["total"] = opening_balance.get(company)
|
||||||
data.append(unclosed)
|
data.append(unclosed)
|
||||||
|
|
||||||
if provisional_profit_loss:
|
if provisional_profit_loss:
|
||||||
@ -102,6 +107,37 @@ def get_balance_sheet_data(fiscal_year, companies, columns, filters):
|
|||||||
|
|
||||||
return data, message, chart, report_summary
|
return data, message, chart, report_summary
|
||||||
|
|
||||||
|
def prepare_companywise_opening_balance(asset_data, liability_data, equity_data, companies):
|
||||||
|
opening_balance = {}
|
||||||
|
for company in companies:
|
||||||
|
opening_value = 0
|
||||||
|
|
||||||
|
# opening_value = Aseet - liability - equity
|
||||||
|
for data in [asset_data, liability_data, equity_data]:
|
||||||
|
account_name = get_root_account_name(data[0].root_type, company)
|
||||||
|
opening_value += get_opening_balance(account_name, data, company)
|
||||||
|
|
||||||
|
opening_balance[company] = opening_value
|
||||||
|
|
||||||
|
if opening_balance:
|
||||||
|
return _("Previous Financial Year is not closed"), opening_balance
|
||||||
|
|
||||||
|
return '', {}
|
||||||
|
|
||||||
|
def get_opening_balance(account_name, data, company):
|
||||||
|
for row in data:
|
||||||
|
if row.get('account_name') == account_name:
|
||||||
|
return row.get('company_wise_opening_bal', {}).get(company, 0.0)
|
||||||
|
|
||||||
|
def get_root_account_name(root_type, company):
|
||||||
|
return frappe.get_all(
|
||||||
|
'Account',
|
||||||
|
fields=['account_name'],
|
||||||
|
filters = {'root_type': root_type, 'is_group': 1,
|
||||||
|
'company': company, 'parent_account': ('is', 'not set')},
|
||||||
|
as_list=1
|
||||||
|
)[0][0]
|
||||||
|
|
||||||
def get_profit_loss_data(fiscal_year, companies, columns, filters):
|
def get_profit_loss_data(fiscal_year, companies, columns, filters):
|
||||||
income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters)
|
income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters)
|
||||||
company_currency = get_company_currency(filters)
|
company_currency = get_company_currency(filters)
|
||||||
@ -193,30 +229,37 @@ def get_account_type_based_data(account_type, companies, fiscal_year, filters):
|
|||||||
data["total"] = total
|
data["total"] = total
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_columns(companies):
|
def get_columns(companies, filters):
|
||||||
columns = [{
|
columns = [
|
||||||
"fieldname": "account",
|
{
|
||||||
"label": _("Account"),
|
"fieldname": "account",
|
||||||
"fieldtype": "Link",
|
"label": _("Account"),
|
||||||
"options": "Account",
|
"fieldtype": "Link",
|
||||||
"width": 300
|
"options": "Account",
|
||||||
}]
|
"width": 300
|
||||||
|
}, {
|
||||||
columns.append({
|
"fieldname": "currency",
|
||||||
"fieldname": "currency",
|
"label": _("Currency"),
|
||||||
"label": _("Currency"),
|
"fieldtype": "Link",
|
||||||
"fieldtype": "Link",
|
"options": "Currency",
|
||||||
"options": "Currency",
|
"hidden": 1
|
||||||
"hidden": 1
|
}
|
||||||
})
|
]
|
||||||
|
|
||||||
for company in companies:
|
for company in companies:
|
||||||
|
apply_currency_formatter = 1 if not filters.presentation_currency else 0
|
||||||
|
currency = filters.presentation_currency
|
||||||
|
if not currency:
|
||||||
|
currency = erpnext.get_company_currency(company)
|
||||||
|
|
||||||
columns.append({
|
columns.append({
|
||||||
"fieldname": company,
|
"fieldname": company,
|
||||||
"label": company,
|
"label": f'{company} ({currency})',
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"options": "currency",
|
"options": "currency",
|
||||||
"width": 150
|
"width": 150,
|
||||||
|
"apply_currency_formatter": apply_currency_formatter,
|
||||||
|
"company_name": company
|
||||||
})
|
})
|
||||||
|
|
||||||
return columns
|
return columns
|
||||||
@ -236,6 +279,8 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i
|
|||||||
start_date = filters.period_start_date if filters.report != 'Balance Sheet' else None
|
start_date = filters.period_start_date if filters.report != 'Balance Sheet' else None
|
||||||
end_date = filters.period_end_date
|
end_date = filters.period_end_date
|
||||||
|
|
||||||
|
filters.end_date = end_date
|
||||||
|
|
||||||
gl_entries_by_account = {}
|
gl_entries_by_account = {}
|
||||||
for root in frappe.db.sql("""select lft, rgt from tabAccount
|
for root in frappe.db.sql("""select lft, rgt from tabAccount
|
||||||
where root_type=%s and ifnull(parent_account, '') = ''""", root_type, as_dict=1):
|
where root_type=%s and ifnull(parent_account, '') = ''""", root_type, as_dict=1):
|
||||||
@ -244,9 +289,10 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i
|
|||||||
end_date, root.lft, root.rgt, filters,
|
end_date, root.lft, root.rgt, filters,
|
||||||
gl_entries_by_account, accounts_by_name, accounts, ignore_closing_entries=False)
|
gl_entries_by_account, accounts_by_name, accounts, ignore_closing_entries=False)
|
||||||
|
|
||||||
calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters)
|
calculate_values(accounts_by_name, gl_entries_by_account, companies, filters, fiscal_year)
|
||||||
accumulate_values_into_parents(accounts, accounts_by_name, companies)
|
accumulate_values_into_parents(accounts, accounts_by_name, companies)
|
||||||
out = prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency)
|
|
||||||
|
out = prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency, filters)
|
||||||
|
|
||||||
if out:
|
if out:
|
||||||
add_total_row(out, root_type, balance_must_be, companies, company_currency)
|
add_total_row(out, root_type, balance_must_be, companies, company_currency)
|
||||||
@ -257,7 +303,10 @@ def get_company_currency(filters=None):
|
|||||||
return (filters.get('presentation_currency')
|
return (filters.get('presentation_currency')
|
||||||
or frappe.get_cached_value('Company', filters.company, "default_currency"))
|
or frappe.get_cached_value('Company', filters.company, "default_currency"))
|
||||||
|
|
||||||
def calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters):
|
def calculate_values(accounts_by_name, gl_entries_by_account, companies, filters, fiscal_year):
|
||||||
|
start_date = (fiscal_year.year_start_date
|
||||||
|
if filters.filter_based_on == 'Fiscal Year' else filters.period_start_date)
|
||||||
|
|
||||||
for entries in gl_entries_by_account.values():
|
for entries in gl_entries_by_account.values():
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
if entry.account_number:
|
if entry.account_number:
|
||||||
@ -266,15 +315,32 @@ def calculate_values(accounts_by_name, gl_entries_by_account, companies, start_d
|
|||||||
account_name = entry.account_name
|
account_name = entry.account_name
|
||||||
|
|
||||||
d = accounts_by_name.get(account_name)
|
d = accounts_by_name.get(account_name)
|
||||||
|
|
||||||
if d:
|
if d:
|
||||||
|
debit, credit = 0, 0
|
||||||
for company in companies:
|
for company in companies:
|
||||||
# check if posting date is within the period
|
# check if posting date is within the period
|
||||||
if (entry.company == company or (filters.get('accumulated_in_group_company'))
|
if (entry.company == company or (filters.get('accumulated_in_group_company'))
|
||||||
and entry.company in companies.get(company)):
|
and entry.company in companies.get(company)):
|
||||||
d[company] = d.get(company, 0.0) + flt(entry.debit) - flt(entry.credit)
|
parent_company_currency = erpnext.get_company_currency(d.company)
|
||||||
|
child_company_currency = erpnext.get_company_currency(entry.company)
|
||||||
|
|
||||||
|
debit, credit = flt(entry.debit), flt(entry.credit)
|
||||||
|
|
||||||
|
if (not filters.get('presentation_currency')
|
||||||
|
and entry.company != company
|
||||||
|
and parent_company_currency != child_company_currency
|
||||||
|
and filters.get('accumulated_in_group_company')):
|
||||||
|
debit = convert(debit, parent_company_currency, child_company_currency, filters.end_date)
|
||||||
|
credit = convert(credit, parent_company_currency, child_company_currency, filters.end_date)
|
||||||
|
|
||||||
|
d[company] = d.get(company, 0.0) + flt(debit) - flt(credit)
|
||||||
|
|
||||||
|
if entry.posting_date < getdate(start_date):
|
||||||
|
d['company_wise_opening_bal'][company] += (flt(debit) - flt(credit))
|
||||||
|
|
||||||
if entry.posting_date < getdate(start_date):
|
if entry.posting_date < getdate(start_date):
|
||||||
d["opening_balance"] = d.get("opening_balance", 0.0) + flt(entry.debit) - flt(entry.credit)
|
d["opening_balance"] = d.get("opening_balance", 0.0) + flt(debit) - flt(credit)
|
||||||
|
|
||||||
def accumulate_values_into_parents(accounts, accounts_by_name, companies):
|
def accumulate_values_into_parents(accounts, accounts_by_name, companies):
|
||||||
"""accumulate children's values in parent accounts"""
|
"""accumulate children's values in parent accounts"""
|
||||||
@ -282,17 +348,18 @@ def accumulate_values_into_parents(accounts, accounts_by_name, companies):
|
|||||||
if d.parent_account:
|
if d.parent_account:
|
||||||
account = d.parent_account_name
|
account = d.parent_account_name
|
||||||
|
|
||||||
if not accounts_by_name.get(account):
|
# if not accounts_by_name.get(account):
|
||||||
continue
|
# continue
|
||||||
|
|
||||||
for company in companies:
|
for company in companies:
|
||||||
accounts_by_name[account][company] = \
|
accounts_by_name[account][company] = \
|
||||||
accounts_by_name[account].get(company, 0.0) + d.get(company, 0.0)
|
accounts_by_name[account].get(company, 0.0) + d.get(company, 0.0)
|
||||||
|
|
||||||
|
accounts_by_name[account]['company_wise_opening_bal'][company] += d.get('company_wise_opening_bal', {}).get(company, 0.0)
|
||||||
|
|
||||||
accounts_by_name[account]["opening_balance"] = \
|
accounts_by_name[account]["opening_balance"] = \
|
||||||
accounts_by_name[account].get("opening_balance", 0.0) + d.get("opening_balance", 0.0)
|
accounts_by_name[account].get("opening_balance", 0.0) + d.get("opening_balance", 0.0)
|
||||||
|
|
||||||
|
|
||||||
def get_account_heads(root_type, companies, filters):
|
def get_account_heads(root_type, companies, filters):
|
||||||
accounts = get_accounts(root_type, filters)
|
accounts = get_accounts(root_type, filters)
|
||||||
|
|
||||||
@ -353,7 +420,7 @@ def get_accounts(root_type, filters):
|
|||||||
`tabAccount` where company = %s and root_type = %s
|
`tabAccount` where company = %s and root_type = %s
|
||||||
""" , (filters.get('company'), root_type), as_dict=1)
|
""" , (filters.get('company'), root_type), as_dict=1)
|
||||||
|
|
||||||
def prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency):
|
def prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency, filters):
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
for d in accounts:
|
for d in accounts:
|
||||||
@ -367,10 +434,13 @@ def prepare_data(accounts, start_date, end_date, balance_must_be, companies, com
|
|||||||
"parent_account": _(d.parent_account),
|
"parent_account": _(d.parent_account),
|
||||||
"indent": flt(d.indent),
|
"indent": flt(d.indent),
|
||||||
"year_start_date": start_date,
|
"year_start_date": start_date,
|
||||||
|
"root_type": d.root_type,
|
||||||
"year_end_date": end_date,
|
"year_end_date": end_date,
|
||||||
"currency": company_currency,
|
"currency": filters.presentation_currency,
|
||||||
|
"company_wise_opening_bal": d.company_wise_opening_bal,
|
||||||
"opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be == "Debit" else -1)
|
"opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be == "Debit" else -1)
|
||||||
})
|
})
|
||||||
|
|
||||||
for company in companies:
|
for company in companies:
|
||||||
if d.get(company) and balance_must_be == "Credit":
|
if d.get(company) and balance_must_be == "Credit":
|
||||||
# change sign based on Debit or Credit, since calculation is done using (debit - credit)
|
# change sign based on Debit or Credit, since calculation is done using (debit - credit)
|
||||||
@ -385,6 +455,7 @@ def prepare_data(accounts, start_date, end_date, balance_must_be, companies, com
|
|||||||
|
|
||||||
row["has_value"] = has_value
|
row["has_value"] = has_value
|
||||||
row["total"] = total
|
row["total"] = total
|
||||||
|
|
||||||
data.append(row)
|
data.append(row)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
@ -447,6 +518,7 @@ def get_account_details(account):
|
|||||||
'is_group', 'account_name', 'account_number', 'parent_account', 'lft', 'rgt'], as_dict=1)
|
'is_group', 'account_name', 'account_number', 'parent_account', 'lft', 'rgt'], as_dict=1)
|
||||||
|
|
||||||
def validate_entries(key, entry, accounts_by_name, accounts):
|
def validate_entries(key, entry, accounts_by_name, accounts):
|
||||||
|
# If an account present in the child company and not in the parent company
|
||||||
if key not in accounts_by_name:
|
if key not in accounts_by_name:
|
||||||
args = get_account_details(entry.account)
|
args = get_account_details(entry.account)
|
||||||
|
|
||||||
@ -456,12 +528,23 @@ def validate_entries(key, entry, accounts_by_name, accounts):
|
|||||||
args.update({
|
args.update({
|
||||||
'lft': parent_args.lft + 1,
|
'lft': parent_args.lft + 1,
|
||||||
'rgt': parent_args.rgt - 1,
|
'rgt': parent_args.rgt - 1,
|
||||||
|
'indent': 3,
|
||||||
'root_type': parent_args.root_type,
|
'root_type': parent_args.root_type,
|
||||||
'report_type': parent_args.report_type
|
'report_type': parent_args.report_type,
|
||||||
|
'parent_account_name': parent_args.account_name,
|
||||||
|
'company_wise_opening_bal': defaultdict(float)
|
||||||
})
|
})
|
||||||
|
|
||||||
accounts_by_name.setdefault(key, args)
|
accounts_by_name.setdefault(key, args)
|
||||||
accounts.append(args)
|
|
||||||
|
idx = len(accounts)
|
||||||
|
# To identify parent account index
|
||||||
|
for index, row in enumerate(accounts):
|
||||||
|
if row.parent_account_name == args.parent_account_name:
|
||||||
|
idx = index
|
||||||
|
break
|
||||||
|
|
||||||
|
accounts.insert(idx+1, args)
|
||||||
|
|
||||||
def get_additional_conditions(from_date, ignore_closing_entries, filters):
|
def get_additional_conditions(from_date, ignore_closing_entries, filters):
|
||||||
additional_conditions = []
|
additional_conditions = []
|
||||||
@ -491,7 +574,6 @@ def add_total_row(out, root_type, balance_must_be, companies, company_currency):
|
|||||||
for company in companies:
|
for company in companies:
|
||||||
total_row.setdefault(company, 0.0)
|
total_row.setdefault(company, 0.0)
|
||||||
total_row[company] += row.get(company, 0.0)
|
total_row[company] += row.get(company, 0.0)
|
||||||
row[company] = 0.0
|
|
||||||
|
|
||||||
total_row.setdefault("total", 0.0)
|
total_row.setdefault("total", 0.0)
|
||||||
total_row["total"] += flt(row["total"])
|
total_row["total"] += flt(row["total"])
|
||||||
@ -511,6 +593,7 @@ def filter_accounts(accounts, depth=10):
|
|||||||
account_name = d.account_number + ' - ' + d.account_name
|
account_name = d.account_number + ' - ' + d.account_name
|
||||||
else:
|
else:
|
||||||
account_name = d.account_name
|
account_name = d.account_name
|
||||||
|
d['company_wise_opening_bal'] = defaultdict(float)
|
||||||
accounts_by_name[account_name] = d
|
accounts_by_name[account_name] = d
|
||||||
|
|
||||||
parent_children_map.setdefault(d.parent_account or None, []).append(d)
|
parent_children_map.setdefault(d.parent_account or None, []).append(d)
|
||||||
|
@ -533,6 +533,17 @@
|
|||||||
"only_for": "United Arab Emirates",
|
"only_for": "United Arab Emirates",
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "GL Entry",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 1,
|
||||||
|
"label": "KSA VAT Report",
|
||||||
|
"link_to": "KSA VAT",
|
||||||
|
"link_type": "Report",
|
||||||
|
"onboard": 0,
|
||||||
|
"only_for": "Saudi Arabia",
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
@ -1153,6 +1164,16 @@
|
|||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "KSA VAT Setting",
|
||||||
|
"link_to": "KSA VAT Setting",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 0,
|
||||||
|
"only_for": "Saudi Arabia",
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
@ -1206,7 +1227,7 @@
|
|||||||
"type": "Link"
|
"type": "Link"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2021-08-27 12:15:52.872470",
|
"modified": "2021-08-26 13:15:52.872470",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounting",
|
"name": "Accounting",
|
||||||
|
@ -11,7 +11,7 @@ frappe.tour['Buying Settings'] = [
|
|||||||
{
|
{
|
||||||
fieldname: "supp_master_name",
|
fieldname: "supp_master_name",
|
||||||
title: "Supplier Naming By",
|
title: "Supplier Naming By",
|
||||||
description: __("By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a ") + "<a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a>" + __(" choose the 'Naming Series' option."),
|
description: __("By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a <a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a> choose the 'Naming Series' option."),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: "buying_price_list",
|
fieldname: "buying_price_list",
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
{
|
||||||
|
"creation": "2021-07-28 11:51:42.319984",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Form Tour",
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": 1,
|
||||||
|
"modified": "2021-10-05 13:06:56.414584",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Buying",
|
||||||
|
"name": "Buying Settings",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_doctype": "Buying Settings",
|
||||||
|
"save_on_complete": 0,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"description": "When a Supplier is saved, system generates a unique identity or name for that Supplier which can be used to refer the Supplier in various Buying transactions.",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "supp_master_name",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"has_next_condition": 0,
|
||||||
|
"is_table_field": 0,
|
||||||
|
"label": "Supplier Naming By",
|
||||||
|
"parent_field": "",
|
||||||
|
"position": "Bottom",
|
||||||
|
"title": "Supplier Naming By"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Configure what should be the default value of Supplier Group when creating a new Supplier.",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "supplier_group",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"has_next_condition": 0,
|
||||||
|
"is_table_field": 0,
|
||||||
|
"label": "Default Supplier Group",
|
||||||
|
"parent_field": "",
|
||||||
|
"position": "Right",
|
||||||
|
"title": "Default Supplier Group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Item prices will be fetched from this Price List.",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "buying_price_list",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"has_next_condition": 0,
|
||||||
|
"is_table_field": 0,
|
||||||
|
"label": "Default Buying Price List",
|
||||||
|
"parent_field": "",
|
||||||
|
"position": "Bottom",
|
||||||
|
"title": "Default Buying Price List"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "If this option is configured \"Yes\", ERPNext will prevent you from creating a Purchase Invoice or a Purchase Receipt directly without creating a Purchase Order first.",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "po_required",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"has_next_condition": 0,
|
||||||
|
"is_table_field": 0,
|
||||||
|
"label": "Is Purchase Order Required for Purchase Invoice & Receipt Creation?",
|
||||||
|
"parent_field": "",
|
||||||
|
"position": "Bottom",
|
||||||
|
"title": "Purchase Order Required"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "If this option is configured \"Yes\", ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first.",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "pr_required",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"has_next_condition": 0,
|
||||||
|
"is_table_field": 0,
|
||||||
|
"label": "Is Purchase Receipt Required for Purchase Invoice Creation?",
|
||||||
|
"parent_field": "",
|
||||||
|
"position": "Bottom",
|
||||||
|
"title": "Purchase Receipt Required"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Buying Settings"
|
||||||
|
}
|
82
erpnext/buying/form_tour/purchase_order/purchase_order.json
Normal file
82
erpnext/buying/form_tour/purchase_order/purchase_order.json
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"creation": "2021-07-29 14:11:58.271113",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Form Tour",
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": 1,
|
||||||
|
"modified": "2021-10-05 13:11:31.436135",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Buying",
|
||||||
|
"name": "Purchase Order",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_doctype": "Purchase Order",
|
||||||
|
"save_on_complete": 1,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"description": "Select a Supplier",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "supplier",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"has_next_condition": 0,
|
||||||
|
"is_table_field": 0,
|
||||||
|
"label": "Supplier",
|
||||||
|
"parent_field": "",
|
||||||
|
"position": "Right",
|
||||||
|
"title": "Supplier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Set the \"Required By\" date for the materials. This sets the \"Required By\" date for all the items.",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "schedule_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"has_next_condition": 0,
|
||||||
|
"is_table_field": 0,
|
||||||
|
"label": "Required By",
|
||||||
|
"parent_field": "",
|
||||||
|
"position": "Left",
|
||||||
|
"title": "Required By"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Items to be purchased can be added here.",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "items",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"has_next_condition": 0,
|
||||||
|
"is_table_field": 0,
|
||||||
|
"label": "Items",
|
||||||
|
"parent_field": "",
|
||||||
|
"position": "Bottom",
|
||||||
|
"title": "Items Table"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"child_doctype": "Purchase Order Item",
|
||||||
|
"description": "Enter the Item Code.",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "item_code",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"has_next_condition": 1,
|
||||||
|
"is_table_field": 1,
|
||||||
|
"label": "Item Code",
|
||||||
|
"next_step_condition": "eval: doc.item_code",
|
||||||
|
"parent_field": "",
|
||||||
|
"parent_fieldname": "items",
|
||||||
|
"position": "Right",
|
||||||
|
"title": "Item Code"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"child_doctype": "Purchase Order Item",
|
||||||
|
"description": "Enter the required quantity for the material.",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"has_next_condition": 0,
|
||||||
|
"is_table_field": 1,
|
||||||
|
"label": "Quantity",
|
||||||
|
"parent_field": "",
|
||||||
|
"parent_fieldname": "items",
|
||||||
|
"position": "Bottom",
|
||||||
|
"title": "Quantity"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Purchase Order"
|
||||||
|
}
|
@ -19,7 +19,7 @@
|
|||||||
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying",
|
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_complete": 0,
|
"is_complete": 0,
|
||||||
"modified": "2020-07-08 14:05:28.273641",
|
"modified": "2021-08-24 18:13:42.463776",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Buying",
|
"name": "Buying",
|
||||||
@ -28,23 +28,11 @@
|
|||||||
{
|
{
|
||||||
"step": "Introduction to Buying"
|
"step": "Introduction to Buying"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"step": "Create a Supplier"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Setup your Warehouse"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Create a Product"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"step": "Create a Material Request"
|
"step": "Create a Material Request"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "Create your first Purchase Order"
|
"step": "Create your first Purchase Order"
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Buying Settings"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"subtitle": "Products, Purchases, Analysis, and more.",
|
"subtitle": "Products, Purchases, Analysis, and more.",
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
{
|
{
|
||||||
"action": "Create Entry",
|
"action": "Show Form Tour",
|
||||||
|
"action_label": "Let\u2019s create your first Material Request",
|
||||||
"creation": "2020-05-15 14:39:09.818764",
|
"creation": "2020-05-15 14:39:09.818764",
|
||||||
|
"description": "# Track Material Request\n\n\nAlso known as Purchase Request or an Indent, is a document identifying a requirement of a set of items (products or services) for various purposes like procurement, transfer, issue, or manufacturing. Once the Material Request is validated, a purchase manager can take the next actions for purchasing items like requesting RFQ from a supplier or directly placing an order with an identified Supplier.\n\n",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Onboarding Step",
|
"doctype": "Onboarding Step",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_complete": 0,
|
"is_complete": 0,
|
||||||
"is_mandatory": 1,
|
|
||||||
"is_single": 0,
|
"is_single": 0,
|
||||||
"is_skipped": 0,
|
"is_skipped": 0,
|
||||||
"modified": "2020-05-15 14:39:09.818764",
|
"modified": "2021-08-24 18:08:08.347501",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"name": "Create a Material Request",
|
"name": "Create a Material Request",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"reference_document": "Material Request",
|
"reference_document": "Material Request",
|
||||||
|
"show_form_tour": 1,
|
||||||
"show_full_form": 1,
|
"show_full_form": 1,
|
||||||
"title": "Create a Material Request",
|
"title": "Track Material Request",
|
||||||
"validate_action": 1
|
"validate_action": 1
|
||||||
}
|
}
|
@ -1,19 +1,21 @@
|
|||||||
{
|
{
|
||||||
"action": "Create Entry",
|
"action": "Show Form Tour",
|
||||||
|
"action_label": "Let\u2019s create your first Purchase Order",
|
||||||
"creation": "2020-05-12 18:17:49.976035",
|
"creation": "2020-05-12 18:17:49.976035",
|
||||||
|
"description": "# Create first Purchase Order\n\nPurchase Order is at the heart of your buying transactions. In ERPNext, Purchase Order can can be created against a Purchase Material Request (indent) and Supplier Quotation as well. Purchase Orders is also linked to Purchase Receipt and Purchase Invoices, allowing you to keep a birds-eye view on your purchase deals.\n\n",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Onboarding Step",
|
"doctype": "Onboarding Step",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_complete": 0,
|
"is_complete": 0,
|
||||||
"is_mandatory": 0,
|
|
||||||
"is_single": 0,
|
"is_single": 0,
|
||||||
"is_skipped": 0,
|
"is_skipped": 0,
|
||||||
"modified": "2020-05-12 18:31:56.856112",
|
"modified": "2021-08-24 18:08:08.936484",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"name": "Create your first Purchase Order",
|
"name": "Create your first Purchase Order",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"reference_document": "Purchase Order",
|
"reference_document": "Purchase Order",
|
||||||
|
"show_form_tour": 0,
|
||||||
"show_full_form": 0,
|
"show_full_form": 0,
|
||||||
"title": "Create your first Purchase Order",
|
"title": "Create first Purchase Order",
|
||||||
"validate_action": 1
|
"validate_action": 1
|
||||||
}
|
}
|
@ -1,19 +1,22 @@
|
|||||||
{
|
{
|
||||||
"action": "Watch Video",
|
"action": "Show Form Tour",
|
||||||
|
"action_label": "Let\u2019s walk-through few Buying Settings",
|
||||||
"creation": "2020-05-06 15:37:09.477765",
|
"creation": "2020-05-06 15:37:09.477765",
|
||||||
|
"description": "# Buying Settings\n\n\nBuying module\u2019s features are highly configurable as per your business needs. Buying Settings is the place where you can set your preferences for:\n\n- Supplier naming and default values\n- Billing and shipping preference in buying transactions\n\n\n",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Onboarding Step",
|
"doctype": "Onboarding Step",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_complete": 0,
|
"is_complete": 0,
|
||||||
"is_mandatory": 0,
|
"is_single": 1,
|
||||||
"is_single": 0,
|
|
||||||
"is_skipped": 0,
|
"is_skipped": 0,
|
||||||
"modified": "2020-05-12 18:25:08.509900",
|
"modified": "2021-08-24 18:08:08.345735",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"name": "Introduction to Buying",
|
"name": "Introduction to Buying",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"show_full_form": 0,
|
"reference_document": "Buying Settings",
|
||||||
"title": "Introduction to Buying",
|
"show_form_tour": 1,
|
||||||
|
"show_full_form": 1,
|
||||||
|
"title": "Buying Settings",
|
||||||
"validate_action": 1,
|
"validate_action": 1,
|
||||||
"video_url": "https://youtu.be/efFajTTQBa8"
|
"video_url": "https://youtu.be/efFajTTQBa8"
|
||||||
}
|
}
|
@ -45,7 +45,6 @@ class TestProcurementTracker(unittest.TestCase):
|
|||||||
pr = make_purchase_receipt(po.name)
|
pr = make_purchase_receipt(po.name)
|
||||||
pr.get("items")[0].cost_center = "Main - _TPC"
|
pr.get("items")[0].cost_center = "Main - _TPC"
|
||||||
pr.submit()
|
pr.submit()
|
||||||
frappe.db.commit()
|
|
||||||
date_obj = datetime.date(datetime.now())
|
date_obj = datetime.date(datetime.now())
|
||||||
|
|
||||||
po.load_from_db()
|
po.load_from_db()
|
||||||
|
@ -591,7 +591,7 @@ def future_sle_exists(args, sl_entries=None):
|
|||||||
|
|
||||||
data = frappe.db.sql("""
|
data = frappe.db.sql("""
|
||||||
select item_code, warehouse, count(name) as total_row
|
select item_code, warehouse, count(name) as total_row
|
||||||
from `tabStock Ledger Entry`
|
from `tabStock Ledger Entry` force index (item_warehouse)
|
||||||
where
|
where
|
||||||
({})
|
({})
|
||||||
and timestamp(posting_date, posting_time)
|
and timestamp(posting_date, posting_time)
|
||||||
|
@ -250,6 +250,7 @@ doc_events = {
|
|||||||
"validate": "erpnext.regional.india.utils.validate_tax_category"
|
"validate": "erpnext.regional.india.utils.validate_tax_category"
|
||||||
},
|
},
|
||||||
"Sales Invoice": {
|
"Sales Invoice": {
|
||||||
|
"after_insert": "erpnext.regional.saudi_arabia.utils.create_qr_code",
|
||||||
"on_submit": [
|
"on_submit": [
|
||||||
"erpnext.regional.create_transaction_log",
|
"erpnext.regional.create_transaction_log",
|
||||||
"erpnext.regional.italy.utils.sales_invoice_on_submit",
|
"erpnext.regional.italy.utils.sales_invoice_on_submit",
|
||||||
@ -259,7 +260,10 @@ doc_events = {
|
|||||||
"erpnext.regional.italy.utils.sales_invoice_on_cancel",
|
"erpnext.regional.italy.utils.sales_invoice_on_cancel",
|
||||||
"erpnext.erpnext_integrations.taxjar_integration.delete_transaction"
|
"erpnext.erpnext_integrations.taxjar_integration.delete_transaction"
|
||||||
],
|
],
|
||||||
"on_trash": "erpnext.regional.check_deletion_permission",
|
"on_trash": [
|
||||||
|
"erpnext.regional.check_deletion_permission",
|
||||||
|
"erpnext.regional.saudi_arabia.utils.delete_qr_code_file"
|
||||||
|
],
|
||||||
"validate": [
|
"validate": [
|
||||||
"erpnext.regional.india.utils.validate_document_name",
|
"erpnext.regional.india.utils.validate_document_name",
|
||||||
"erpnext.regional.india.utils.update_taxable_values"
|
"erpnext.regional.india.utils.update_taxable_values"
|
||||||
|
@ -74,7 +74,6 @@ class TestDailyWorkSummary(unittest.TestCase):
|
|||||||
from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r \
|
from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r \
|
||||||
where q.name = r.parent""", as_dict=1)
|
where q.name = r.parent""", as_dict=1)
|
||||||
|
|
||||||
frappe.db.commit()
|
|
||||||
|
|
||||||
def setup_groups(self, hour=None):
|
def setup_groups(self, hour=None):
|
||||||
# setup email to trigger at this hour
|
# setup email to trigger at this hour
|
||||||
|
@ -10,6 +10,26 @@ frappe.ui.form.on('Expense Claim', {
|
|||||||
},
|
},
|
||||||
company: function(frm) {
|
company: function(frm) {
|
||||||
erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
|
erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
|
||||||
|
var expenses = frm.doc.expenses;
|
||||||
|
for (var i = 0; i < expenses.length; i++) {
|
||||||
|
var expense = expenses[i];
|
||||||
|
if (!expense.expense_type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.hr.doctype.expense_claim.expense_claim.get_expense_claim_account_and_cost_center",
|
||||||
|
args: {
|
||||||
|
"expense_claim_type": expense.expense_type,
|
||||||
|
"company": frm.doc.company
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (r.message) {
|
||||||
|
expense.default_account = r.message.account;
|
||||||
|
expense.cost_center = r.message.cost_center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -334,7 +334,6 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.is_secured_loan",
|
"depends_on": "eval:doc.is_secured_loan",
|
||||||
"fetch_from": "loan_application.maximum_loan_amount",
|
|
||||||
"fieldname": "maximum_loan_amount",
|
"fieldname": "maximum_loan_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Maximum Loan Amount",
|
"label": "Maximum Loan Amount",
|
||||||
@ -360,7 +359,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-04-19 18:10:32.360818",
|
"modified": "2021-10-12 18:10:32.360818",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Loan Management",
|
"module": "Loan Management",
|
||||||
"name": "Loan",
|
"name": "Loan",
|
||||||
|
@ -137,16 +137,23 @@ class Loan(AccountsController):
|
|||||||
frappe.throw(_("Loan amount is mandatory"))
|
frappe.throw(_("Loan amount is mandatory"))
|
||||||
|
|
||||||
def link_loan_security_pledge(self):
|
def link_loan_security_pledge(self):
|
||||||
if self.is_secured_loan:
|
if self.is_secured_loan and self.loan_application:
|
||||||
loan_security_pledge = frappe.db.get_value('Loan Security Pledge', {'loan_application': self.loan_application},
|
maximum_loan_value = frappe.db.get_value('Loan Security Pledge',
|
||||||
'name')
|
{
|
||||||
|
'loan_application': self.loan_application,
|
||||||
|
'status': 'Requested'
|
||||||
|
},
|
||||||
|
'sum(maximum_loan_value)'
|
||||||
|
)
|
||||||
|
|
||||||
if loan_security_pledge:
|
if maximum_loan_value:
|
||||||
frappe.db.set_value('Loan Security Pledge', loan_security_pledge, {
|
frappe.db.sql("""
|
||||||
'loan': self.name,
|
UPDATE `tabLoan Security Pledge`
|
||||||
'status': 'Pledged',
|
SET loan = %s, pledge_time = %s, status = 'Pledged'
|
||||||
'pledge_time': now_datetime()
|
WHERE status = 'Requested' and loan_application = %s
|
||||||
})
|
""", (self.name, now_datetime(), self.loan_application))
|
||||||
|
|
||||||
|
self.db_set('maximum_loan_amount', maximum_loan_value)
|
||||||
|
|
||||||
def unlink_loan_security_pledge(self):
|
def unlink_loan_security_pledge(self):
|
||||||
pledges = frappe.get_all('Loan Security Pledge', fields=['name'], filters={'loan': self.name})
|
pledges = frappe.get_all('Loan Security Pledge', fields=['name'], filters={'loan': self.name})
|
||||||
|
@ -130,10 +130,11 @@ class LoanApplication(Document):
|
|||||||
def create_loan(source_name, target_doc=None, submit=0):
|
def create_loan(source_name, target_doc=None, submit=0):
|
||||||
def update_accounts(source_doc, target_doc, source_parent):
|
def update_accounts(source_doc, target_doc, source_parent):
|
||||||
account_details = frappe.get_all("Loan Type",
|
account_details = frappe.get_all("Loan Type",
|
||||||
fields=["mode_of_payment", "payment_account","loan_account", "interest_income_account", "penalty_income_account"],
|
fields=["mode_of_payment", "payment_account","loan_account", "interest_income_account", "penalty_income_account"],
|
||||||
filters = {'name': source_doc.loan_type}
|
filters = {'name': source_doc.loan_type})[0]
|
||||||
)[0]
|
|
||||||
|
|
||||||
|
if source_doc.is_secured_loan:
|
||||||
|
target_doc.maximum_loan_amount = 0
|
||||||
|
|
||||||
target_doc.mode_of_payment = account_details.mode_of_payment
|
target_doc.mode_of_payment = account_details.mode_of_payment
|
||||||
target_doc.payment_account = account_details.payment_account
|
target_doc.payment_account = account_details.payment_account
|
||||||
|
@ -198,7 +198,7 @@ def get_disbursal_amount(loan, on_current_security_price=0):
|
|||||||
security_value = get_total_pledged_security_value(loan)
|
security_value = get_total_pledged_security_value(loan)
|
||||||
|
|
||||||
if loan_details.is_secured_loan and not on_current_security_price:
|
if loan_details.is_secured_loan and not on_current_security_price:
|
||||||
security_value = flt(loan_details.maximum_loan_amount)
|
security_value = get_maximum_amount_as_per_pledged_security(loan)
|
||||||
|
|
||||||
if not security_value and not loan_details.is_secured_loan:
|
if not security_value and not loan_details.is_secured_loan:
|
||||||
security_value = flt(loan_details.loan_amount)
|
security_value = flt(loan_details.loan_amount)
|
||||||
@ -209,3 +209,6 @@ def get_disbursal_amount(loan, on_current_security_price=0):
|
|||||||
disbursal_amount = loan_details.loan_amount - loan_details.disbursed_amount
|
disbursal_amount = loan_details.loan_amount - loan_details.disbursed_amount
|
||||||
|
|
||||||
return disbursal_amount
|
return disbursal_amount
|
||||||
|
|
||||||
|
def get_maximum_amount_as_per_pledged_security(loan):
|
||||||
|
return flt(frappe.db.get_value('Loan Security Pledge', {'loan': loan}, 'sum(maximum_loan_value)'))
|
||||||
|
@ -411,7 +411,7 @@ def get_amounts(amounts, against_loan, posting_date):
|
|||||||
if due_date and not final_due_date:
|
if due_date and not final_due_date:
|
||||||
final_due_date = add_days(due_date, loan_type_details.grace_period_in_days)
|
final_due_date = add_days(due_date, loan_type_details.grace_period_in_days)
|
||||||
|
|
||||||
if against_loan_doc.status in ('Disbursed', 'Loan Closure Requested', 'Closed'):
|
if against_loan_doc.status in ('Disbursed', 'Closed') or against_loan_doc.disbursed_amount >= against_loan_doc.loan_amount:
|
||||||
pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid \
|
pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid \
|
||||||
- against_loan_doc.total_interest_payable - against_loan_doc.written_off_amount
|
- against_loan_doc.total_interest_payable - against_loan_doc.written_off_amount
|
||||||
else:
|
else:
|
||||||
|
@ -1133,8 +1133,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
query_filters["has_variants"] = 0
|
query_filters["has_variants"] = 0
|
||||||
|
|
||||||
if filters and filters.get("is_stock_item"):
|
if filters and filters.get("is_stock_item"):
|
||||||
or_cond_filters["is_stock_item"] = 1
|
query_filters["is_stock_item"] = 1
|
||||||
or_cond_filters["has_variants"] = 1
|
|
||||||
|
|
||||||
return frappe.get_list("Item",
|
return frappe.get_list("Item",
|
||||||
fields = fields, filters=query_filters,
|
fields = fields, filters=query_filters,
|
||||||
|
@ -4,13 +4,14 @@
|
|||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.test_runner import make_test_records
|
from frappe.test_runner import make_test_records
|
||||||
from frappe.utils import cstr, flt
|
from frappe.utils import cstr, flt
|
||||||
|
|
||||||
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
|
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
|
||||||
from erpnext.manufacturing.doctype.bom.bom import make_variant_bom
|
from erpnext.manufacturing.doctype.bom.bom import item_query, make_variant_bom
|
||||||
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
|
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
||||||
@ -375,6 +376,16 @@ class TestBOM(unittest.TestCase):
|
|||||||
# FG Items in Scrap/Loss Table should have Is Process Loss set
|
# FG Items in Scrap/Loss Table should have Is Process Loss set
|
||||||
self.assertRaises(frappe.ValidationError, bom_doc.submit)
|
self.assertRaises(frappe.ValidationError, bom_doc.submit)
|
||||||
|
|
||||||
|
def test_bom_item_query(self):
|
||||||
|
query = partial(item_query, doctype="Item", txt="", searchfield="name", start=0, page_len=20, filters={"is_stock_item": 1})
|
||||||
|
|
||||||
|
test_items = query(txt="_Test")
|
||||||
|
filtered = query(txt="_Test Item 2")
|
||||||
|
|
||||||
|
self.assertNotEqual(len(test_items), len(filtered), msg="Item filtering showing excessive results")
|
||||||
|
self.assertTrue(0 < len(filtered) <= 3, msg="Item filtering showing excessive results")
|
||||||
|
|
||||||
|
|
||||||
def get_default_bom(item_code="_Test FG Item 2"):
|
def get_default_bom(item_code="_Test FG Item 2"):
|
||||||
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
|
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
|
||||||
|
|
||||||
|
@ -246,7 +246,7 @@ erpnext.patches.v13_0.update_payment_terms_outstanding
|
|||||||
erpnext.patches.v12_0.add_state_code_for_ladakh
|
erpnext.patches.v12_0.add_state_code_for_ladakh
|
||||||
erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
|
erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
|
||||||
erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes
|
erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes
|
||||||
erpnext.patches.v12_0.update_vehicle_no_reqd_condition
|
erpnext.patches.v13_0.update_vehicle_no_reqd_condition
|
||||||
erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation
|
erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation
|
||||||
erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
|
erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
|
||||||
erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae
|
erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae
|
||||||
@ -299,7 +299,7 @@ erpnext.patches.v13_0.gst_fields_for_pos_invoice
|
|||||||
erpnext.patches.v13_0.create_accounting_dimensions_in_pos_doctypes
|
erpnext.patches.v13_0.create_accounting_dimensions_in_pos_doctypes
|
||||||
erpnext.patches.v13_0.trim_sales_invoice_custom_field_length
|
erpnext.patches.v13_0.trim_sales_invoice_custom_field_length
|
||||||
erpnext.patches.v13_0.create_custom_field_for_finance_book
|
erpnext.patches.v13_0.create_custom_field_for_finance_book
|
||||||
erpnext.patches.v13_0.modify_invalid_gain_loss_gl_entries
|
erpnext.patches.v13_0.modify_invalid_gain_loss_gl_entries #2
|
||||||
erpnext.patches.v13_0.fix_additional_cost_in_mfg_stock_entry
|
erpnext.patches.v13_0.fix_additional_cost_in_mfg_stock_entry
|
||||||
erpnext.patches.v13_0.set_status_in_maintenance_schedule_table
|
erpnext.patches.v13_0.set_status_in_maintenance_schedule_table
|
||||||
erpnext.patches.v13_0.add_default_interview_notification_templates
|
erpnext.patches.v13_0.add_default_interview_notification_templates
|
||||||
|
@ -17,7 +17,7 @@ def execute():
|
|||||||
where
|
where
|
||||||
ref_exchange_rate = 1
|
ref_exchange_rate = 1
|
||||||
and docstatus = 1
|
and docstatus = 1
|
||||||
and ifnull(exchange_gain_loss, '') != ''
|
and ifnull(exchange_gain_loss, 0) != 0
|
||||||
group by
|
group by
|
||||||
parent
|
parent
|
||||||
""", as_dict=1)
|
""", as_dict=1)
|
||||||
@ -30,7 +30,7 @@ def execute():
|
|||||||
where
|
where
|
||||||
ref_exchange_rate = 1
|
ref_exchange_rate = 1
|
||||||
and docstatus = 1
|
and docstatus = 1
|
||||||
and ifnull(exchange_gain_loss, '') != ''
|
and ifnull(exchange_gain_loss, 0) != 0
|
||||||
group by
|
group by
|
||||||
parent
|
parent
|
||||||
""", as_dict=1)
|
""", as_dict=1)
|
||||||
@ -38,12 +38,24 @@ def execute():
|
|||||||
if purchase_invoices + sales_invoices:
|
if purchase_invoices + sales_invoices:
|
||||||
frappe.log_error(json.dumps(purchase_invoices + sales_invoices, indent=2), title="Patch Log")
|
frappe.log_error(json.dumps(purchase_invoices + sales_invoices, indent=2), title="Patch Log")
|
||||||
|
|
||||||
|
acc_frozen_upto = frappe.db.get_value('Accounts Settings', None, 'acc_frozen_upto')
|
||||||
|
if acc_frozen_upto:
|
||||||
|
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
|
||||||
|
|
||||||
for invoice in purchase_invoices + sales_invoices:
|
for invoice in purchase_invoices + sales_invoices:
|
||||||
doc = frappe.get_doc(invoice.type, invoice.name)
|
try:
|
||||||
doc.docstatus = 2
|
doc = frappe.get_doc(invoice.type, invoice.name)
|
||||||
doc.make_gl_entries()
|
doc.docstatus = 2
|
||||||
for advance in doc.advances:
|
doc.make_gl_entries()
|
||||||
if advance.ref_exchange_rate == 1:
|
for advance in doc.advances:
|
||||||
advance.db_set('exchange_gain_loss', 0, False)
|
if advance.ref_exchange_rate == 1:
|
||||||
doc.docstatus = 1
|
advance.db_set('exchange_gain_loss', 0, False)
|
||||||
doc.make_gl_entries()
|
doc.docstatus = 1
|
||||||
|
doc.make_gl_entries()
|
||||||
|
frappe.db.commit()
|
||||||
|
except Exception:
|
||||||
|
frappe.db.rollback()
|
||||||
|
print(f'Failed to correct gl entries of {invoice.name}')
|
||||||
|
|
||||||
|
if acc_frozen_upto:
|
||||||
|
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', acc_frozen_upto)
|
@ -2,7 +2,7 @@ import frappe
|
|||||||
|
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
frappe.reload_doc('custom', 'doctype', 'custom_field')
|
frappe.reload_doc('custom', 'doctype', 'custom_field', force=True)
|
||||||
company = frappe.get_all('Company', filters = {'country': 'India'})
|
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||||
if not company:
|
if not company:
|
||||||
return
|
return
|
@ -31,3 +31,4 @@ def create_transaction_log(doc, method):
|
|||||||
"document_name": doc.name,
|
"document_name": doc.name,
|
||||||
"data": data
|
"data": data
|
||||||
}).insert(ignore_permissions=True)
|
}).insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
@ -6,8 +6,10 @@
|
|||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"gst_summary",
|
"gst_summary",
|
||||||
"column_break_2",
|
"gst_tax_settings_section",
|
||||||
"round_off_gst_values",
|
"round_off_gst_values",
|
||||||
|
"column_break_4",
|
||||||
|
"hsn_wise_tax_breakup",
|
||||||
"gstin_email_sent_on",
|
"gstin_email_sent_on",
|
||||||
"section_break_4",
|
"section_break_4",
|
||||||
"gst_accounts",
|
"gst_accounts",
|
||||||
@ -17,37 +19,23 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "gst_summary",
|
"fieldname": "gst_summary",
|
||||||
"fieldtype": "HTML",
|
"fieldtype": "HTML",
|
||||||
"label": "GST Summary",
|
"label": "GST Summary"
|
||||||
"show_days": 1,
|
|
||||||
"show_seconds": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "column_break_2",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"show_days": 1,
|
|
||||||
"show_seconds": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "gstin_email_sent_on",
|
"fieldname": "gstin_email_sent_on",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "GSTIN Email Sent On",
|
"label": "GSTIN Email Sent On",
|
||||||
"read_only": 1,
|
"read_only": 1
|
||||||
"show_days": 1,
|
|
||||||
"show_seconds": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "section_break_4",
|
"fieldname": "section_break_4",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break"
|
||||||
"show_days": 1,
|
|
||||||
"show_seconds": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "gst_accounts",
|
"fieldname": "gst_accounts",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "GST Accounts",
|
"label": "GST Accounts",
|
||||||
"options": "GST Account",
|
"options": "GST Account"
|
||||||
"show_days": 1,
|
|
||||||
"show_seconds": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "250000",
|
"default": "250000",
|
||||||
@ -56,24 +44,35 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "B2C Limit",
|
"label": "B2C Limit",
|
||||||
"reqd": 1,
|
"reqd": 1
|
||||||
"show_days": 1,
|
|
||||||
"show_seconds": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"description": "Enabling this option will round off individual GST components in all the Invoices",
|
"description": "Enabling this option will round off individual GST components in all the Invoices",
|
||||||
"fieldname": "round_off_gst_values",
|
"fieldname": "round_off_gst_values",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Round Off GST Values",
|
"label": "Round Off GST Values"
|
||||||
"show_days": 1,
|
},
|
||||||
"show_seconds": 1
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "hsn_wise_tax_breakup",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Tax Breakup Table Based On HSN Code"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "gst_tax_settings_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "GST Tax Settings"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_4",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-01-28 17:19:47.969260",
|
"modified": "2021-10-11 18:10:14.242614",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Regional",
|
"module": "Regional",
|
||||||
"name": "GST Settings",
|
"name": "GST Settings",
|
||||||
@ -83,4 +82,4 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"creation": "2021-07-13 09:17:09.862163",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"title",
|
||||||
|
"item_tax_template",
|
||||||
|
"account"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Account",
|
||||||
|
"options": "Account",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "title",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Title",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "item_tax_template",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Item Tax Template",
|
||||||
|
"options": "Item Tax Template",
|
||||||
|
"reqd": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2021-08-04 06:42:38.205597",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Regional",
|
||||||
|
"name": "KSA VAT Purchase Account",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, Havenir Solutions and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class KSAVATPurchaseAccount(Document):
|
||||||
|
pass
|
@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (c) 2021, Havenir Solutions and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('KSA VAT Sales Account', {
|
||||||
|
// refresh: function(frm) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
});
|
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"creation": "2021-07-13 08:46:33.820968",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"title",
|
||||||
|
"item_tax_template",
|
||||||
|
"account"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Account",
|
||||||
|
"options": "Account",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "title",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Title",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "item_tax_template",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Item Tax Template",
|
||||||
|
"options": "Item Tax Template",
|
||||||
|
"reqd": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2021-08-04 06:42:00.081407",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Regional",
|
||||||
|
"name": "KSA VAT Sales Account",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, Havenir Solutions and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class KSAVATSalesAccount(Document):
|
||||||
|
pass
|
@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, Havenir Solutions and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestKSAVATSalesAccount(unittest.TestCase):
|
||||||
|
pass
|
@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (c) 2021, Havenir Solutions and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('KSA VAT Setting', {
|
||||||
|
onload: function () {
|
||||||
|
frappe.breadcrumbs.add('Accounts', 'KSA VAT Setting');
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"autoname": "field:company",
|
||||||
|
"creation": "2021-07-13 08:49:01.100356",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"company",
|
||||||
|
"ksa_vat_sales_accounts",
|
||||||
|
"ksa_vat_purchase_accounts"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Company",
|
||||||
|
"options": "Company",
|
||||||
|
"reqd": 1,
|
||||||
|
"unique": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "ksa_vat_sales_accounts",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"label": "KSA VAT Sales Accounts",
|
||||||
|
"options": "KSA VAT Sales Account",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "ksa_vat_purchase_accounts",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"label": "KSA VAT Purchase Accounts",
|
||||||
|
"options": "KSA VAT Purchase Account",
|
||||||
|
"reqd": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [],
|
||||||
|
"modified": "2021-08-26 04:29:06.499378",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Regional",
|
||||||
|
"name": "KSA VAT Setting",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"title_field": "company",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, Havenir Solutions and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class KSAVATSetting(Document):
|
||||||
|
pass
|
@ -0,0 +1,5 @@
|
|||||||
|
frappe.listview_settings['KSA VAT Setting'] = {
|
||||||
|
onload () {
|
||||||
|
frappe.breadcrumbs.add('Accounts');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, Havenir Solutions and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestKSAVATSetting(unittest.TestCase):
|
||||||
|
pass
|
@ -112,7 +112,11 @@ def validate_gstin_check_digit(gstin, label='GSTIN'):
|
|||||||
frappe.throw(_("""Invalid {0}! The check digit validation has failed. Please ensure you've typed the {0} correctly.""").format(label))
|
frappe.throw(_("""Invalid {0}! The check digit validation has failed. Please ensure you've typed the {0} correctly.""").format(label))
|
||||||
|
|
||||||
def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
|
def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
|
||||||
return [_("Item"), _("Taxable Amount")] + tax_accounts
|
hsn_wise_in_gst_settings = frappe.db.get_single_value('GST Settings','hsn_wise_tax_breakup')
|
||||||
|
if frappe.get_meta(item_doctype).has_field('gst_hsn_code') and hsn_wise_in_gst_settings:
|
||||||
|
return [_("HSN/SAC"), _("Taxable Amount")] + tax_accounts
|
||||||
|
else:
|
||||||
|
return [_("Item"), _("Taxable Amount")] + tax_accounts
|
||||||
|
|
||||||
def get_itemised_tax_breakup_data(doc, account_wise=False, hsn_wise=False):
|
def get_itemised_tax_breakup_data(doc, account_wise=False, hsn_wise=False):
|
||||||
itemised_tax = get_itemised_tax(doc.taxes, with_tax_account=account_wise)
|
itemised_tax = get_itemised_tax(doc.taxes, with_tax_account=account_wise)
|
||||||
@ -122,14 +126,17 @@ def get_itemised_tax_breakup_data(doc, account_wise=False, hsn_wise=False):
|
|||||||
if not frappe.get_meta(doc.doctype + " Item").has_field('gst_hsn_code'):
|
if not frappe.get_meta(doc.doctype + " Item").has_field('gst_hsn_code'):
|
||||||
return itemised_tax, itemised_taxable_amount
|
return itemised_tax, itemised_taxable_amount
|
||||||
|
|
||||||
if hsn_wise:
|
hsn_wise_in_gst_settings = frappe.db.get_single_value('GST Settings','hsn_wise_tax_breakup')
|
||||||
|
|
||||||
|
tax_breakup_hsn_wise = hsn_wise or hsn_wise_in_gst_settings
|
||||||
|
if tax_breakup_hsn_wise:
|
||||||
item_hsn_map = frappe._dict()
|
item_hsn_map = frappe._dict()
|
||||||
for d in doc.items:
|
for d in doc.items:
|
||||||
item_hsn_map.setdefault(d.item_code or d.item_name, d.get("gst_hsn_code"))
|
item_hsn_map.setdefault(d.item_code or d.item_name, d.get("gst_hsn_code"))
|
||||||
|
|
||||||
hsn_tax = {}
|
hsn_tax = {}
|
||||||
for item, taxes in itemised_tax.items():
|
for item, taxes in itemised_tax.items():
|
||||||
item_or_hsn = item if not hsn_wise else item_hsn_map.get(item)
|
item_or_hsn = item if not tax_breakup_hsn_wise else item_hsn_map.get(item)
|
||||||
hsn_tax.setdefault(item_or_hsn, frappe._dict())
|
hsn_tax.setdefault(item_or_hsn, frappe._dict())
|
||||||
for tax_desc, tax_detail in taxes.items():
|
for tax_desc, tax_detail in taxes.items():
|
||||||
key = tax_desc
|
key = tax_desc
|
||||||
@ -142,7 +149,7 @@ def get_itemised_tax_breakup_data(doc, account_wise=False, hsn_wise=False):
|
|||||||
# set taxable amount
|
# set taxable amount
|
||||||
hsn_taxable_amount = frappe._dict()
|
hsn_taxable_amount = frappe._dict()
|
||||||
for item in itemised_taxable_amount:
|
for item in itemised_taxable_amount:
|
||||||
item_or_hsn = item if not hsn_wise else item_hsn_map.get(item)
|
item_or_hsn = item if not tax_breakup_hsn_wise else item_hsn_map.get(item)
|
||||||
hsn_taxable_amount.setdefault(item_or_hsn, 0)
|
hsn_taxable_amount.setdefault(item_or_hsn, 0)
|
||||||
hsn_taxable_amount[item_or_hsn] += itemised_taxable_amount.get(item)
|
hsn_taxable_amount[item_or_hsn] += itemised_taxable_amount.get(item)
|
||||||
|
|
||||||
|
0
erpnext/regional/report/ksa_vat/__init__.py
Normal file
0
erpnext/regional/report/ksa_vat/__init__.py
Normal file
60
erpnext/regional/report/ksa_vat/ksa_vat.js
Normal file
60
erpnext/regional/report/ksa_vat/ksa_vat.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// Copyright (c) 2016, Havenir Solutions and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
frappe.query_reports["KSA VAT"] = {
|
||||||
|
onload() {
|
||||||
|
frappe.breadcrumbs.add('Accounts');
|
||||||
|
},
|
||||||
|
"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(), -1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "to_date",
|
||||||
|
"label": __("To Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"reqd": 1,
|
||||||
|
"default": frappe.datetime.get_today()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"formatter": function(value, row, column, data, default_formatter) {
|
||||||
|
if (data
|
||||||
|
&& (data.title=='VAT on Sales' || data.title=='VAT on Purchases')
|
||||||
|
&& data.title==value) {
|
||||||
|
value = $(`<span>${value}</span>`);
|
||||||
|
var $value = $(value).css("font-weight", "bold");
|
||||||
|
value = $value.wrap("<p></p>").parent().html();
|
||||||
|
return value
|
||||||
|
}else if (data.title=='Grand Total'){
|
||||||
|
if (data.title==value) {
|
||||||
|
value = $(`<span>${value}</span>`);
|
||||||
|
var $value = $(value).css("font-weight", "bold");
|
||||||
|
value = $value.wrap("<p></p>").parent().html();
|
||||||
|
return value
|
||||||
|
}else{
|
||||||
|
value = default_formatter(value, row, column, data);
|
||||||
|
value = $(`<span>${value}</span>`);
|
||||||
|
var $value = $(value).css("font-weight", "bold");
|
||||||
|
value = $value.wrap("<p></p>").parent().html();
|
||||||
|
console.log($value)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
value = default_formatter(value, row, column, data);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
32
erpnext/regional/report/ksa_vat/ksa_vat.json
Normal file
32
erpnext/regional/report/ksa_vat/ksa_vat.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"add_total_row": 0,
|
||||||
|
"columns": [],
|
||||||
|
"creation": "2021-07-13 08:54:38.000949",
|
||||||
|
"disable_prepared_report": 1,
|
||||||
|
"disabled": 1,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Report",
|
||||||
|
"filters": [],
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": "Yes",
|
||||||
|
"modified": "2021-08-26 04:14:37.202594",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Regional",
|
||||||
|
"name": "KSA VAT",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"prepared_report": 1,
|
||||||
|
"ref_doctype": "GL Entry",
|
||||||
|
"report_name": "KSA VAT",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"role": "System Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Accounts Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Accounts User"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
176
erpnext/regional/report/ksa_vat/ksa_vat.py
Normal file
176
erpnext/regional/report/ksa_vat/ksa_vat.py
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
# Copyright (c) 2013, Havenir Solutions and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import get_url_to_list
|
||||||
|
|
||||||
|
|
||||||
|
def execute(filters=None):
|
||||||
|
columns = columns = get_columns()
|
||||||
|
data = get_data(filters)
|
||||||
|
return columns, data
|
||||||
|
|
||||||
|
def get_columns():
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"fieldname": "title",
|
||||||
|
"label": _("Title"),
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 300
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "amount",
|
||||||
|
"label": _("Amount (SAR)"),
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "adjustment_amount",
|
||||||
|
"label": _("Adjustment (SAR)"),
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "vat_amount",
|
||||||
|
"label": _("VAT Amount (SAR)"),
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 150,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_data(filters):
|
||||||
|
data = []
|
||||||
|
|
||||||
|
# Validate if vat settings exist
|
||||||
|
company = filters.get('company')
|
||||||
|
if frappe.db.exists('KSA VAT Setting', company) is None:
|
||||||
|
url = get_url_to_list('KSA VAT Setting')
|
||||||
|
frappe.msgprint(_('Create <a href="{}">KSA VAT Setting</a> for this company').format(url))
|
||||||
|
return data
|
||||||
|
|
||||||
|
ksa_vat_setting = frappe.get_doc('KSA VAT Setting', company)
|
||||||
|
|
||||||
|
# Sales Heading
|
||||||
|
append_data(data, 'VAT on Sales', '', '', '')
|
||||||
|
|
||||||
|
grand_total_taxable_amount = 0
|
||||||
|
grand_total_taxable_adjustment_amount = 0
|
||||||
|
grand_total_tax = 0
|
||||||
|
|
||||||
|
for vat_setting in ksa_vat_setting.ksa_vat_sales_accounts:
|
||||||
|
total_taxable_amount, total_taxable_adjustment_amount, \
|
||||||
|
total_tax = get_tax_data_for_each_vat_setting(vat_setting, filters, 'Sales Invoice')
|
||||||
|
|
||||||
|
# Adding results to data
|
||||||
|
append_data(data, vat_setting.title, total_taxable_amount,
|
||||||
|
total_taxable_adjustment_amount, total_tax)
|
||||||
|
|
||||||
|
grand_total_taxable_amount += total_taxable_amount
|
||||||
|
grand_total_taxable_adjustment_amount += total_taxable_adjustment_amount
|
||||||
|
grand_total_tax += total_tax
|
||||||
|
|
||||||
|
# Sales Grand Total
|
||||||
|
append_data(data, 'Grand Total', grand_total_taxable_amount,
|
||||||
|
grand_total_taxable_adjustment_amount, grand_total_tax)
|
||||||
|
|
||||||
|
# Blank Line
|
||||||
|
append_data(data, '', '', '', '')
|
||||||
|
|
||||||
|
# Purchase Heading
|
||||||
|
append_data(data, 'VAT on Purchases', '', '', '')
|
||||||
|
|
||||||
|
grand_total_taxable_amount = 0
|
||||||
|
grand_total_taxable_adjustment_amount = 0
|
||||||
|
grand_total_tax = 0
|
||||||
|
|
||||||
|
for vat_setting in ksa_vat_setting.ksa_vat_purchase_accounts:
|
||||||
|
total_taxable_amount, total_taxable_adjustment_amount, \
|
||||||
|
total_tax = get_tax_data_for_each_vat_setting(vat_setting, filters, 'Purchase Invoice')
|
||||||
|
|
||||||
|
# Adding results to data
|
||||||
|
append_data(data, vat_setting.title, total_taxable_amount,
|
||||||
|
total_taxable_adjustment_amount, total_tax)
|
||||||
|
|
||||||
|
grand_total_taxable_amount += total_taxable_amount
|
||||||
|
grand_total_taxable_adjustment_amount += total_taxable_adjustment_amount
|
||||||
|
grand_total_tax += total_tax
|
||||||
|
|
||||||
|
# Purchase Grand Total
|
||||||
|
append_data(data, 'Grand Total', grand_total_taxable_amount,
|
||||||
|
grand_total_taxable_adjustment_amount, grand_total_tax)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_tax_data_for_each_vat_setting(vat_setting, filters, doctype):
|
||||||
|
'''
|
||||||
|
(KSA, {filters}, 'Sales Invoice') => 500, 153, 10 \n
|
||||||
|
calculates and returns \n
|
||||||
|
total_taxable_amount, total_taxable_adjustment_amount, total_tax'''
|
||||||
|
from_date = filters.get('from_date')
|
||||||
|
to_date = filters.get('to_date')
|
||||||
|
|
||||||
|
# Initiate variables
|
||||||
|
total_taxable_amount = 0
|
||||||
|
total_taxable_adjustment_amount = 0
|
||||||
|
total_tax = 0
|
||||||
|
# Fetch All Invoices
|
||||||
|
invoices = frappe.get_list(doctype,
|
||||||
|
filters ={
|
||||||
|
'docstatus': 1,
|
||||||
|
'posting_date': ['between', [from_date, to_date]]
|
||||||
|
}, fields =['name', 'is_return'])
|
||||||
|
|
||||||
|
for invoice in invoices:
|
||||||
|
invoice_items = frappe.get_list(f'{doctype} Item',
|
||||||
|
filters ={
|
||||||
|
'docstatus': 1,
|
||||||
|
'parent': invoice.name,
|
||||||
|
'item_tax_template': vat_setting.item_tax_template
|
||||||
|
}, fields =['item_code', 'net_amount'])
|
||||||
|
|
||||||
|
for item in invoice_items:
|
||||||
|
# Summing up total taxable amount
|
||||||
|
if invoice.is_return == 0:
|
||||||
|
total_taxable_amount += item.net_amount
|
||||||
|
|
||||||
|
if invoice.is_return == 1:
|
||||||
|
total_taxable_adjustment_amount += item.net_amount
|
||||||
|
|
||||||
|
# Summing up total tax
|
||||||
|
total_tax += get_tax_amount(item.item_code, vat_setting.account, doctype, invoice.name)
|
||||||
|
|
||||||
|
return total_taxable_amount, total_taxable_adjustment_amount, total_tax
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def append_data(data, title, amount, adjustment_amount, vat_amount):
|
||||||
|
"""Returns data with appended value."""
|
||||||
|
data.append({"title": _(title), "amount": amount, "adjustment_amount": adjustment_amount, "vat_amount": vat_amount})
|
||||||
|
|
||||||
|
def get_tax_amount(item_code, account_head, doctype, parent):
|
||||||
|
if doctype == 'Sales Invoice':
|
||||||
|
tax_doctype = 'Sales Taxes and Charges'
|
||||||
|
|
||||||
|
elif doctype == 'Purchase Invoice':
|
||||||
|
tax_doctype = 'Purchase Taxes and Charges'
|
||||||
|
|
||||||
|
item_wise_tax_detail = frappe.get_value(tax_doctype, {
|
||||||
|
'docstatus': 1,
|
||||||
|
'parent': parent,
|
||||||
|
'account_head': account_head
|
||||||
|
}, 'item_wise_tax_detail')
|
||||||
|
|
||||||
|
tax_amount = 0
|
||||||
|
if item_wise_tax_detail and len(item_wise_tax_detail) > 0:
|
||||||
|
item_wise_tax_detail = json.loads(item_wise_tax_detail)
|
||||||
|
for key, value in item_wise_tax_detail.items():
|
||||||
|
if key == item_code:
|
||||||
|
tax_amount = value[1]
|
||||||
|
break
|
||||||
|
|
||||||
|
return tax_amount
|
@ -2,10 +2,36 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
from erpnext.regional.united_arab_emirates.setup import make_custom_fields, add_print_formats
|
from frappe.permissions import add_permission, update_permission_property
|
||||||
|
from erpnext.regional.united_arab_emirates.setup import make_custom_fields as uae_custom_fields, add_print_formats
|
||||||
|
from erpnext.regional.saudi_arabia.wizard.operations.setup_ksa_vat_setting import create_ksa_vat_setting
|
||||||
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
|
||||||
|
|
||||||
def setup(company=None, patch=True):
|
def setup(company=None, patch=True):
|
||||||
make_custom_fields()
|
uae_custom_fields()
|
||||||
add_print_formats()
|
add_print_formats()
|
||||||
|
add_permissions()
|
||||||
|
create_ksa_vat_setting(company)
|
||||||
|
make_qrcode_field()
|
||||||
|
|
||||||
|
def add_permissions():
|
||||||
|
"""Add Permissions for KSA VAT Setting."""
|
||||||
|
add_permission('KSA VAT Setting', 'All', 0)
|
||||||
|
for role in ('Accounts Manager', 'Accounts User', 'System Manager'):
|
||||||
|
add_permission('KSA VAT Setting', role, 0)
|
||||||
|
update_permission_property('KSA VAT Setting', role, 0, 'write', 1)
|
||||||
|
update_permission_property('KSA VAT Setting', role, 0, 'create', 1)
|
||||||
|
|
||||||
|
"""Enable KSA VAT Report"""
|
||||||
|
frappe.db.set_value('Report', 'KSA VAT', 'disabled', 0)
|
||||||
|
|
||||||
|
def make_qrcode_field():
|
||||||
|
"""Created QR code Image file"""
|
||||||
|
qr_code_field = dict(
|
||||||
|
fieldname='qr_code',
|
||||||
|
label='QR Code',
|
||||||
|
fieldtype='Attach Image',
|
||||||
|
read_only=1, no_copy=1, hidden=1)
|
||||||
|
|
||||||
|
create_custom_field('Sales Invoice', qr_code_field)
|
||||||
|
77
erpnext/regional/saudi_arabia/utils.py
Normal file
77
erpnext/regional/saudi_arabia/utils.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import io
|
||||||
|
import os
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from pyqrcode import create as qr_create
|
||||||
|
|
||||||
|
from erpnext import get_region
|
||||||
|
|
||||||
|
|
||||||
|
def create_qr_code(doc, method):
|
||||||
|
"""Create QR Code after inserting Sales Inv
|
||||||
|
"""
|
||||||
|
|
||||||
|
region = get_region(doc.company)
|
||||||
|
if region not in ['Saudi Arabia']:
|
||||||
|
return
|
||||||
|
|
||||||
|
# if QR Code field not present, do nothing
|
||||||
|
if not hasattr(doc, 'qr_code'):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Don't create QR Code if it already exists
|
||||||
|
qr_code = doc.get("qr_code")
|
||||||
|
if qr_code and frappe.db.exists({"doctype": "File", "file_url": qr_code}):
|
||||||
|
return
|
||||||
|
|
||||||
|
meta = frappe.get_meta('Sales Invoice')
|
||||||
|
|
||||||
|
for field in meta.get_image_fields():
|
||||||
|
if field.fieldname == 'qr_code':
|
||||||
|
# Creating public url to print format
|
||||||
|
default_print_format = frappe.db.get_value('Property Setter', dict(property='default_print_format', doc_type=doc.doctype), "value")
|
||||||
|
|
||||||
|
# System Language
|
||||||
|
language = frappe.get_system_settings('language')
|
||||||
|
|
||||||
|
# creating qr code for the url
|
||||||
|
url = f"{ frappe.utils.get_url() }/{ doc.doctype }/{ doc.name }?format={ default_print_format or 'Standard' }&_lang={ language }&key={ doc.get_signature() }"
|
||||||
|
qr_image = io.BytesIO()
|
||||||
|
url = qr_create(url, error='L')
|
||||||
|
url.png(qr_image, scale=2, quiet_zone=1)
|
||||||
|
|
||||||
|
# making file
|
||||||
|
filename = f"QR-CODE-{doc.name}.png".replace(os.path.sep, "__")
|
||||||
|
_file = frappe.get_doc({
|
||||||
|
"doctype": "File",
|
||||||
|
"file_name": filename,
|
||||||
|
"is_private": 0,
|
||||||
|
"content": qr_image.getvalue(),
|
||||||
|
"attached_to_doctype": doc.get("doctype"),
|
||||||
|
"attached_to_name": doc.get("name"),
|
||||||
|
"attached_to_field": "qr_code"
|
||||||
|
})
|
||||||
|
|
||||||
|
_file.save()
|
||||||
|
|
||||||
|
# assigning to document
|
||||||
|
doc.db_set('qr_code', _file.file_url)
|
||||||
|
doc.notify_update()
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def delete_qr_code_file(doc, method):
|
||||||
|
"""Delete QR Code on deleted sales invoice"""
|
||||||
|
|
||||||
|
region = get_region(doc.company)
|
||||||
|
if region not in ['Saudi Arabia']:
|
||||||
|
return
|
||||||
|
|
||||||
|
if hasattr(doc, 'qr_code'):
|
||||||
|
if doc.get('qr_code'):
|
||||||
|
file_doc = frappe.get_list('File', {
|
||||||
|
'file_url': doc.get('qr_code')
|
||||||
|
})
|
||||||
|
if len(file_doc):
|
||||||
|
frappe.delete_doc('File', file_doc[0].name)
|
0
erpnext/regional/saudi_arabia/wizard/__init__.py
Normal file
0
erpnext/regional/saudi_arabia/wizard/__init__.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "Sales Account",
|
||||||
|
"accounts": [
|
||||||
|
{
|
||||||
|
"title": "Standard rated Sales",
|
||||||
|
"item_tax_template": "KSA VAT 5%",
|
||||||
|
"account": "VAT 5%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Zero rated domestic sales",
|
||||||
|
"item_tax_template": "KSA VAT Zero",
|
||||||
|
"account": "VAT Zero"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Exempted sales",
|
||||||
|
"item_tax_template": "KSA VAT Exempted",
|
||||||
|
"account": "VAT Zero"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Purchase Account",
|
||||||
|
"accounts": [
|
||||||
|
{
|
||||||
|
"title": "Standard rated domestic purchases",
|
||||||
|
"item_tax_template": "KSA VAT 5%",
|
||||||
|
"account": "VAT 5%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Imports subject to VAT paid at customs",
|
||||||
|
"item_tax_template": "KSA Excise 50%",
|
||||||
|
"account": "Excise 50%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Zero rated purchases",
|
||||||
|
"item_tax_template": "KSA VAT Zero",
|
||||||
|
"account": "VAT Zero"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Exempted purchases",
|
||||||
|
"item_tax_template": "KSA VAT Exempted",
|
||||||
|
"account": "VAT Zero"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
@ -0,0 +1,46 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
from erpnext.setup.setup_wizard.operations.taxes_setup import setup_taxes_and_charges
|
||||||
|
|
||||||
|
|
||||||
|
def create_ksa_vat_setting(company):
|
||||||
|
"""On creation of first company. Creates KSA VAT Setting"""
|
||||||
|
|
||||||
|
company = frappe.get_doc('Company', company)
|
||||||
|
setup_taxes_and_charges(company.name, company.country)
|
||||||
|
|
||||||
|
file_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'ksa_vat_settings.json')
|
||||||
|
with open(file_path, 'r') as json_file:
|
||||||
|
account_data = json.load(json_file)
|
||||||
|
|
||||||
|
# Creating KSA VAT Setting
|
||||||
|
ksa_vat_setting = frappe.get_doc({
|
||||||
|
'doctype': 'KSA VAT Setting',
|
||||||
|
'company': company.name
|
||||||
|
})
|
||||||
|
|
||||||
|
for data in account_data:
|
||||||
|
if data['type'] == 'Sales Account':
|
||||||
|
for row in data['accounts']:
|
||||||
|
item_tax_template = row['item_tax_template']
|
||||||
|
account = row['account']
|
||||||
|
ksa_vat_setting.append('ksa_vat_sales_accounts', {
|
||||||
|
'title': row['title'],
|
||||||
|
'item_tax_template': f'{item_tax_template} - {company.abbr}',
|
||||||
|
'account': f'{account} - {company.abbr}'
|
||||||
|
})
|
||||||
|
|
||||||
|
elif data['type'] == 'Purchase Account':
|
||||||
|
for row in data['accounts']:
|
||||||
|
item_tax_template = row['item_tax_template']
|
||||||
|
account = row['account']
|
||||||
|
ksa_vat_setting.append('ksa_vat_purchase_accounts', {
|
||||||
|
'title': row['title'],
|
||||||
|
'item_tax_template': f'{item_tax_template} - {company.abbr}',
|
||||||
|
'account': f'{account} - {company.abbr}'
|
||||||
|
})
|
||||||
|
|
||||||
|
ksa_vat_setting.save()
|
@ -1382,7 +1382,6 @@ def make_sales_order_workflow():
|
|||||||
|
|
||||||
frappe.get_doc(dict(doctype='Role', role_name='Test Junior Approver')).insert(ignore_if_duplicate=True)
|
frappe.get_doc(dict(doctype='Role', role_name='Test Junior Approver')).insert(ignore_if_duplicate=True)
|
||||||
frappe.get_doc(dict(doctype='Role', role_name='Test Approver')).insert(ignore_if_duplicate=True)
|
frappe.get_doc(dict(doctype='Role', role_name='Test Approver')).insert(ignore_if_duplicate=True)
|
||||||
frappe.db.commit()
|
|
||||||
frappe.cache().hdel('roles', frappe.session.user)
|
frappe.cache().hdel('roles', frappe.session.user)
|
||||||
|
|
||||||
workflow = frappe.get_doc({
|
workflow = frappe.get_doc({
|
||||||
|
@ -44,7 +44,6 @@ class TestShoppingCartSettings(unittest.TestCase):
|
|||||||
|
|
||||||
def test_tax_rule_validation(self):
|
def test_tax_rule_validation(self):
|
||||||
frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0")
|
frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0")
|
||||||
frappe.db.commit()
|
|
||||||
|
|
||||||
cart_settings = self.get_cart_settings()
|
cart_settings = self.get_cart_settings()
|
||||||
cart_settings.enabled = 1
|
cart_settings.enabled = 1
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import (
|
from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import (
|
||||||
@ -18,10 +15,19 @@ def show_cart_count():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def set_cart_count(login_manager):
|
def set_cart_count(login_manager):
|
||||||
role, parties = check_customer_or_supplier()
|
# since this is run only on hooks login event
|
||||||
if role == 'Supplier': return
|
# make sure user is already a customer
|
||||||
|
# before trying to set cart count
|
||||||
|
user_is_customer = is_customer()
|
||||||
|
if not user_is_customer:
|
||||||
|
return
|
||||||
|
|
||||||
if show_cart_count():
|
if show_cart_count():
|
||||||
from erpnext.shopping_cart.cart import set_cart_count
|
from erpnext.shopping_cart.cart import set_cart_count
|
||||||
|
|
||||||
|
# set_cart_count will try to fetch existing cart quotation
|
||||||
|
# or create one if non existent (and create a customer too)
|
||||||
|
# cart count is calculated from this quotation's items
|
||||||
set_cart_count()
|
set_cart_count()
|
||||||
|
|
||||||
def clear_cart_count(login_manager):
|
def clear_cart_count(login_manager):
|
||||||
@ -32,13 +38,13 @@ def update_website_context(context):
|
|||||||
cart_enabled = is_cart_enabled()
|
cart_enabled = is_cart_enabled()
|
||||||
context["shopping_cart_enabled"] = cart_enabled
|
context["shopping_cart_enabled"] = cart_enabled
|
||||||
|
|
||||||
def check_customer_or_supplier():
|
def is_customer():
|
||||||
if frappe.session.user:
|
if frappe.session.user and frappe.session.user != "Guest":
|
||||||
contact_name = frappe.get_value("Contact", {"email_id": frappe.session.user})
|
contact_name = frappe.get_value("Contact", {"email_id": frappe.session.user})
|
||||||
if contact_name:
|
if contact_name:
|
||||||
contact = frappe.get_doc('Contact', contact_name)
|
contact = frappe.get_doc('Contact', contact_name)
|
||||||
for link in contact.links:
|
for link in contact.links:
|
||||||
if link.link_doctype in ('Customer', 'Supplier'):
|
if link.link_doctype == 'Customer':
|
||||||
return link.link_doctype, link.link_name
|
return True
|
||||||
|
|
||||||
return 'Customer', None
|
return False
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.exceptions import ValidationError
|
from frappe.exceptions import ValidationError
|
||||||
@ -11,9 +8,10 @@ from frappe.utils import cint, flt
|
|||||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||||
from erpnext.stock.doctype.batch.batch import UnableToSelectBatchError, get_batch_no, get_batch_qty
|
from erpnext.stock.doctype.batch.batch import UnableToSelectBatchError, get_batch_no, get_batch_qty
|
||||||
from erpnext.stock.get_item_details import get_item_details
|
from erpnext.stock.get_item_details import get_item_details
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestBatch(unittest.TestCase):
|
class TestBatch(ERPNextTestCase):
|
||||||
def test_item_has_batch_enabled(self):
|
def test_item_has_batch_enabled(self):
|
||||||
self.assertRaises(ValidationError, frappe.get_doc({
|
self.assertRaises(ValidationError, frappe.get_doc({
|
||||||
"doctype": "Batch",
|
"doctype": "Batch",
|
||||||
|
@ -14,51 +14,6 @@ class Bin(Document):
|
|||||||
self.stock_uom = frappe.get_cached_value('Item', self.item_code, 'stock_uom')
|
self.stock_uom = frappe.get_cached_value('Item', self.item_code, 'stock_uom')
|
||||||
self.set_projected_qty()
|
self.set_projected_qty()
|
||||||
|
|
||||||
def update_stock(self, args, allow_negative_stock=False, via_landed_cost_voucher=False):
|
|
||||||
'''Called from erpnext.stock.utils.update_bin'''
|
|
||||||
self.update_qty(args)
|
|
||||||
|
|
||||||
if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
|
|
||||||
from erpnext.stock.stock_ledger import update_entries_after, update_qty_in_future_sle
|
|
||||||
|
|
||||||
if not args.get("posting_date"):
|
|
||||||
args["posting_date"] = nowdate()
|
|
||||||
|
|
||||||
if args.get("is_cancelled") and via_landed_cost_voucher:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Reposts only current voucher SL Entries
|
|
||||||
# Updates valuation rate, stock value, stock queue for current transaction
|
|
||||||
update_entries_after({
|
|
||||||
"item_code": self.item_code,
|
|
||||||
"warehouse": self.warehouse,
|
|
||||||
"posting_date": args.get("posting_date"),
|
|
||||||
"posting_time": args.get("posting_time"),
|
|
||||||
"voucher_type": args.get("voucher_type"),
|
|
||||||
"voucher_no": args.get("voucher_no"),
|
|
||||||
"sle_id": args.name,
|
|
||||||
"creation": args.creation
|
|
||||||
}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
|
|
||||||
|
|
||||||
# update qty in future ale and Validate negative qty
|
|
||||||
update_qty_in_future_sle(args, allow_negative_stock)
|
|
||||||
|
|
||||||
|
|
||||||
def update_qty(self, args):
|
|
||||||
# update the stock values (for current quantities)
|
|
||||||
if args.get("voucher_type")=="Stock Reconciliation":
|
|
||||||
self.actual_qty = args.get("qty_after_transaction")
|
|
||||||
else:
|
|
||||||
self.actual_qty = flt(self.actual_qty) + flt(args.get("actual_qty"))
|
|
||||||
|
|
||||||
self.ordered_qty = flt(self.ordered_qty) + flt(args.get("ordered_qty"))
|
|
||||||
self.reserved_qty = flt(self.reserved_qty) + flt(args.get("reserved_qty"))
|
|
||||||
self.indented_qty = flt(self.indented_qty) + flt(args.get("indented_qty"))
|
|
||||||
self.planned_qty = flt(self.planned_qty) + flt(args.get("planned_qty"))
|
|
||||||
|
|
||||||
self.set_projected_qty()
|
|
||||||
self.db_update()
|
|
||||||
|
|
||||||
def set_projected_qty(self):
|
def set_projected_qty(self):
|
||||||
self.projected_qty = (flt(self.actual_qty) + flt(self.ordered_qty)
|
self.projected_qty = (flt(self.actual_qty) + flt(self.ordered_qty)
|
||||||
+ flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
|
+ flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
|
||||||
@ -143,3 +98,67 @@ class Bin(Document):
|
|||||||
|
|
||||||
def on_doctype_update():
|
def on_doctype_update():
|
||||||
frappe.db.add_index("Bin", ["item_code", "warehouse"])
|
frappe.db.add_index("Bin", ["item_code", "warehouse"])
|
||||||
|
|
||||||
|
|
||||||
|
def update_stock(bin_name, args, allow_negative_stock=False, via_landed_cost_voucher=False):
|
||||||
|
'''Called from erpnext.stock.utils.update_bin'''
|
||||||
|
update_qty(bin_name, args)
|
||||||
|
|
||||||
|
if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
|
||||||
|
from erpnext.stock.stock_ledger import update_entries_after, update_qty_in_future_sle
|
||||||
|
|
||||||
|
if not args.get("posting_date"):
|
||||||
|
args["posting_date"] = nowdate()
|
||||||
|
|
||||||
|
if args.get("is_cancelled") and via_landed_cost_voucher:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Reposts only current voucher SL Entries
|
||||||
|
# Updates valuation rate, stock value, stock queue for current transaction
|
||||||
|
update_entries_after({
|
||||||
|
"item_code": args.get('item_code'),
|
||||||
|
"warehouse": args.get('warehouse'),
|
||||||
|
"posting_date": args.get("posting_date"),
|
||||||
|
"posting_time": args.get("posting_time"),
|
||||||
|
"voucher_type": args.get("voucher_type"),
|
||||||
|
"voucher_no": args.get("voucher_no"),
|
||||||
|
"sle_id": args.get('name'),
|
||||||
|
"creation": args.get('creation')
|
||||||
|
}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
|
||||||
|
|
||||||
|
# update qty in future sle and Validate negative qty
|
||||||
|
update_qty_in_future_sle(args, allow_negative_stock)
|
||||||
|
|
||||||
|
def get_bin_details(bin_name):
|
||||||
|
return frappe.db.get_value('Bin', bin_name, ['actual_qty', 'ordered_qty',
|
||||||
|
'reserved_qty', 'indented_qty', 'planned_qty', 'reserved_qty_for_production',
|
||||||
|
'reserved_qty_for_sub_contract'], as_dict=1)
|
||||||
|
|
||||||
|
def update_qty(bin_name, args):
|
||||||
|
bin_details = get_bin_details(bin_name)
|
||||||
|
|
||||||
|
# update the stock values (for current quantities)
|
||||||
|
if args.get("voucher_type")=="Stock Reconciliation":
|
||||||
|
actual_qty = args.get('qty_after_transaction')
|
||||||
|
else:
|
||||||
|
actual_qty = bin_details.actual_qty + flt(args.get("actual_qty"))
|
||||||
|
|
||||||
|
ordered_qty = flt(bin_details.ordered_qty) + flt(args.get("ordered_qty"))
|
||||||
|
reserved_qty = flt(bin_details.reserved_qty) + flt(args.get("reserved_qty"))
|
||||||
|
indented_qty = flt(bin_details.indented_qty) + flt(args.get("indented_qty"))
|
||||||
|
planned_qty = flt(bin_details.planned_qty) + flt(args.get("planned_qty"))
|
||||||
|
|
||||||
|
|
||||||
|
# compute projected qty
|
||||||
|
projected_qty = (flt(actual_qty) + flt(ordered_qty)
|
||||||
|
+ flt(indented_qty) + flt(planned_qty) - flt(reserved_qty)
|
||||||
|
- flt(bin_details.reserved_qty_for_production) - flt(bin_details.reserved_qty_for_sub_contract))
|
||||||
|
|
||||||
|
frappe.db.set_value('Bin', bin_name, {
|
||||||
|
'actual_qty': actual_qty,
|
||||||
|
'ordered_qty': ordered_qty,
|
||||||
|
'reserved_qty': reserved_qty,
|
||||||
|
'indented_qty': indented_qty,
|
||||||
|
'planned_qty': planned_qty,
|
||||||
|
'projected_qty': projected_qty
|
||||||
|
})
|
@ -5,7 +5,6 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import cstr, flt, nowdate, nowtime
|
from frappe.utils import cstr, flt, nowdate, nowtime
|
||||||
@ -37,9 +36,10 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import
|
|||||||
)
|
)
|
||||||
from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
|
from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
|
||||||
from erpnext.stock.stock_ledger import get_previous_sle
|
from erpnext.stock.stock_ledger import get_previous_sle
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestDeliveryNote(unittest.TestCase):
|
class TestDeliveryNote(ERPNextTestCase):
|
||||||
def test_over_billing_against_dn(self):
|
def test_over_billing_against_dn(self):
|
||||||
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
|
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
|
||||||
|
|
||||||
|
@ -14,11 +14,12 @@ from erpnext.stock.doctype.delivery_trip.delivery_trip import (
|
|||||||
make_expense_claim,
|
make_expense_claim,
|
||||||
notify_customers,
|
notify_customers,
|
||||||
)
|
)
|
||||||
from erpnext.tests.utils import create_test_contact_and_address
|
from erpnext.tests.utils import ERPNextTestCase, create_test_contact_and_address
|
||||||
|
|
||||||
|
|
||||||
class TestDeliveryTrip(unittest.TestCase):
|
class TestDeliveryTrip(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
driver = create_driver()
|
driver = create_driver()
|
||||||
create_vehicle()
|
create_vehicle()
|
||||||
create_delivery_notification()
|
create_delivery_notification()
|
||||||
@ -32,6 +33,7 @@ class TestDeliveryTrip(unittest.TestCase):
|
|||||||
frappe.db.sql("delete from `tabVehicle`")
|
frappe.db.sql("delete from `tabVehicle`")
|
||||||
frappe.db.sql("delete from `tabEmail Template`")
|
frappe.db.sql("delete from `tabEmail Template`")
|
||||||
frappe.db.sql("delete from `tabDelivery Trip`")
|
frappe.db.sql("delete from `tabDelivery Trip`")
|
||||||
|
return super().tearDown()
|
||||||
|
|
||||||
def test_expense_claim_fields_are_fetched_properly(self):
|
def test_expense_claim_fields_are_fetched_properly(self):
|
||||||
expense_claim = make_expense_claim(self.delivery_trip.name)
|
expense_claim = make_expense_claim(self.delivery_trip.name)
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.test_runner import make_test_objects
|
from frappe.test_runner import make_test_objects
|
||||||
@ -25,7 +24,7 @@ from erpnext.stock.doctype.item.item import (
|
|||||||
)
|
)
|
||||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
from erpnext.stock.get_item_details import get_item_details
|
from erpnext.stock.get_item_details import get_item_details
|
||||||
from erpnext.tests.utils import change_settings
|
from erpnext.tests.utils import ERPNextTestCase, change_settings
|
||||||
|
|
||||||
test_ignore = ["BOM"]
|
test_ignore = ["BOM"]
|
||||||
test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"]
|
test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"]
|
||||||
@ -53,8 +52,9 @@ def make_item(item_code, properties=None):
|
|||||||
|
|
||||||
return item
|
return item
|
||||||
|
|
||||||
class TestItem(unittest.TestCase):
|
class TestItem(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
frappe.flags.attribute_values = None
|
frappe.flags.attribute_values = None
|
||||||
|
|
||||||
def get_item(self, idx):
|
def get_item(self, idx):
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
@ -21,10 +20,12 @@ from erpnext.stock.doctype.item.test_item import create_item
|
|||||||
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
||||||
create_stock_reconciliation,
|
create_stock_reconciliation,
|
||||||
)
|
)
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestItemAlternative(unittest.TestCase):
|
class TestItemAlternative(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
make_items()
|
make_items()
|
||||||
|
|
||||||
def test_alternative_item_for_subcontract_rm(self):
|
def test_alternative_item_for_subcontract_rm(self):
|
||||||
|
@ -3,17 +3,17 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
test_records = frappe.get_test_records('Item Attribute')
|
test_records = frappe.get_test_records('Item Attribute')
|
||||||
|
|
||||||
from erpnext.stock.doctype.item_attribute.item_attribute import ItemAttributeIncrementError
|
from erpnext.stock.doctype.item_attribute.item_attribute import ItemAttributeIncrementError
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestItemAttribute(unittest.TestCase):
|
class TestItemAttribute(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
if frappe.db.exists("Item Attribute", "_Test_Length"):
|
if frappe.db.exists("Item Attribute", "_Test_Length"):
|
||||||
frappe.delete_doc("Item Attribute", "_Test_Length")
|
frappe.delete_doc("Item Attribute", "_Test_Length")
|
||||||
|
|
||||||
|
@ -3,17 +3,17 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.test_runner import make_test_records_for_doctype
|
from frappe.test_runner import make_test_records_for_doctype
|
||||||
|
|
||||||
from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem
|
from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem
|
||||||
from erpnext.stock.get_item_details import get_price_list_rate_for, process_args
|
from erpnext.stock.get_item_details import get_price_list_rate_for, process_args
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestItemPrice(unittest.TestCase):
|
class TestItemPrice(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
frappe.db.sql("delete from `tabItem Price`")
|
frappe.db.sql("delete from `tabItem Price`")
|
||||||
make_test_records_for_doctype("Item Price", force=True)
|
make_test_records_for_doctype("Item Price", force=True)
|
||||||
|
|
||||||
|
@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
|
|
||||||
@ -16,9 +14,10 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import (
|
|||||||
get_gl_entries,
|
get_gl_entries,
|
||||||
make_purchase_receipt,
|
make_purchase_receipt,
|
||||||
)
|
)
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestLandedCostVoucher(unittest.TestCase):
|
class TestLandedCostVoucher(ERPNextTestCase):
|
||||||
def test_landed_cost_voucher(self):
|
def test_landed_cost_voucher(self):
|
||||||
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
|
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
|
||||||
|
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import flt, today
|
from frappe.utils import flt, today
|
||||||
|
|
||||||
@ -18,9 +16,10 @@ from erpnext.stock.doctype.material_request.material_request import (
|
|||||||
make_supplier_quotation,
|
make_supplier_quotation,
|
||||||
raise_work_orders,
|
raise_work_orders,
|
||||||
)
|
)
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestMaterialRequest(unittest.TestCase):
|
class TestMaterialRequest(ERPNextTestCase):
|
||||||
def test_make_purchase_order(self):
|
def test_make_purchase_order(self):
|
||||||
mr = frappe.copy_doc(test_records[0]).insert()
|
mr = frappe.copy_doc(test_records[0]).insert()
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ from __future__ import unicode_literals
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
# test_records = frappe.get_test_records('Packing Slip')
|
# test_records = frappe.get_test_records('Packing Slip')
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestPackingSlip(unittest.TestCase):
|
class TestPackingSlip(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch']
|
test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch']
|
||||||
@ -15,9 +13,10 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_pu
|
|||||||
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
|
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
|
||||||
EmptyStockReconciliationItemsError,
|
EmptyStockReconciliationItemsError,
|
||||||
)
|
)
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestPickList(unittest.TestCase):
|
class TestPickList(ERPNextTestCase):
|
||||||
|
|
||||||
def test_pick_list_picks_warehouse_for_each_item(self):
|
def test_pick_list_picks_warehouse_for_each_item(self):
|
||||||
try:
|
try:
|
||||||
|
@ -17,9 +17,10 @@ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchas
|
|||||||
from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError, get_serial_nos
|
from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError, get_serial_nos
|
||||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||||
from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction
|
from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestPurchaseReceipt(unittest.TestCase):
|
class TestPurchaseReceipt(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
|
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
|
||||||
|
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from erpnext.stock.doctype.batch.test_batch import make_new_batch
|
from erpnext.stock.doctype.batch.test_batch import make_new_batch
|
||||||
@ -13,9 +11,10 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_pu
|
|||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||||
from erpnext.stock.get_item_details import get_conversion_factor
|
from erpnext.stock.get_item_details import get_conversion_factor
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestPutawayRule(unittest.TestCase):
|
class TestPutawayRule(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
if not frappe.db.exists("Item", "_Rice"):
|
if not frappe.db.exists("Item", "_Rice"):
|
||||||
make_item("_Rice", {
|
make_item("_Rice", {
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import nowdate
|
from frappe.utils import nowdate
|
||||||
|
|
||||||
@ -15,12 +13,14 @@ from erpnext.controllers.stock_controller import (
|
|||||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||||
from erpnext.stock.doctype.item.test_item import create_item
|
from erpnext.stock.doctype.item.test_item import create_item
|
||||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
# test_records = frappe.get_test_records('Quality Inspection')
|
# test_records = frappe.get_test_records('Quality Inspection')
|
||||||
|
|
||||||
|
|
||||||
class TestQualityInspection(unittest.TestCase):
|
class TestQualityInspection(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
create_item("_Test Item with QA")
|
create_item("_Test Item with QA")
|
||||||
frappe.db.set_value(
|
frappe.db.set_value(
|
||||||
"Item", "_Test Item with QA", "inspection_required_before_delivery", 1
|
"Item", "_Test Item with QA", "inspection_required_before_delivery", 1
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||||
@ -20,9 +18,10 @@ test_dependencies = ["Item"]
|
|||||||
test_records = frappe.get_test_records('Serial No')
|
test_records = frappe.get_test_records('Serial No')
|
||||||
|
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import *
|
from erpnext.stock.doctype.serial_no.serial_no import *
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestSerialNo(unittest.TestCase):
|
class TestSerialNo(ERPNextTestCase):
|
||||||
def test_cannot_create_direct(self):
|
def test_cannot_create_direct(self):
|
||||||
frappe.delete_doc_if_exists("Serial No", "_TCSER0001")
|
frappe.delete_doc_if_exists("Serial No", "_TCSER0001")
|
||||||
|
|
||||||
|
@ -3,15 +3,15 @@
|
|||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import unittest
|
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from erpnext.stock.doctype.delivery_note.delivery_note import make_shipment
|
from erpnext.stock.doctype.delivery_note.delivery_note import make_shipment
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestShipment(unittest.TestCase):
|
class TestShipment(ERPNextTestCase):
|
||||||
def test_shipment_from_delivery_note(self):
|
def test_shipment_from_delivery_note(self):
|
||||||
delivery_note = create_test_delivery_note()
|
delivery_note = create_test_delivery_note()
|
||||||
delivery_note.submit()
|
delivery_note.submit()
|
||||||
@ -47,7 +47,6 @@ def create_test_delivery_note():
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
delivery_note.insert()
|
delivery_note.insert()
|
||||||
frappe.db.commit()
|
|
||||||
return delivery_note
|
return delivery_note
|
||||||
|
|
||||||
|
|
||||||
@ -91,7 +90,6 @@ def create_test_shipment(delivery_notes = None):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
shipment.insert()
|
shipment.insert()
|
||||||
frappe.db.commit()
|
|
||||||
return shipment
|
return shipment
|
||||||
|
|
||||||
|
|
||||||
|
@ -317,7 +317,7 @@
|
|||||||
"in_create": 1,
|
"in_create": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-10-08 12:42:51.857631",
|
"modified": "2021-10-08 13:42:51.857631",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Stock Ledger Entry",
|
"name": "Stock Ledger Entry",
|
||||||
|
@ -181,3 +181,4 @@ def on_doctype_update():
|
|||||||
|
|
||||||
frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"])
|
frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"])
|
||||||
frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"])
|
frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"])
|
||||||
|
frappe.db.add_index("Stock Ledger Entry", ["warehouse", "item_code"], "item_warehouse")
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.core.page.permission_manager.permission_manager import reset
|
from frappe.core.page.permission_manager.permission_manager import reset
|
||||||
from frappe.utils import add_days, today
|
from frappe.utils import add_days, today
|
||||||
@ -21,9 +19,10 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import
|
|||||||
create_stock_reconciliation,
|
create_stock_reconciliation,
|
||||||
)
|
)
|
||||||
from erpnext.stock.stock_ledger import get_previous_sle
|
from erpnext.stock.stock_ledger import get_previous_sle
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestStockLedgerEntry(unittest.TestCase):
|
class TestStockLedgerEntry(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
items = create_items()
|
items = create_items()
|
||||||
reset('Stock Entry')
|
reset('Stock Entry')
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import add_days, flt, nowdate, nowtime, random_string
|
from frappe.utils import add_days, flt, nowdate, nowtime, random_string
|
||||||
|
|
||||||
@ -22,12 +20,13 @@ from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
|
|||||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||||
from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
|
from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
|
||||||
from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method
|
from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method
|
||||||
from erpnext.tests.utils import change_settings
|
from erpnext.tests.utils import ERPNextTestCase, change_settings
|
||||||
|
|
||||||
|
|
||||||
class TestStockReconciliation(unittest.TestCase):
|
class TestStockReconciliation(ERPNextTestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(self):
|
def setUpClass(self):
|
||||||
|
super().setUpClass()
|
||||||
create_batch_or_serial_no_items()
|
create_batch_or_serial_no_items()
|
||||||
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
|
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
|
||||||
|
|
||||||
@ -372,7 +371,6 @@ class TestStockReconciliation(unittest.TestCase):
|
|||||||
"""
|
"""
|
||||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||||
from erpnext.stock.stock_ledger import NegativeStockError
|
from erpnext.stock.stock_ledger import NegativeStockError
|
||||||
frappe.db.commit()
|
|
||||||
|
|
||||||
item_code = "Backdated-Reco-Cancellation-Item"
|
item_code = "Backdated-Reco-Cancellation-Item"
|
||||||
warehouse = "_Test Warehouse - _TC"
|
warehouse = "_Test Warehouse - _TC"
|
||||||
@ -395,10 +393,6 @@ class TestStockReconciliation(unittest.TestCase):
|
|||||||
repost_exists = bool(frappe.db.exists("Repost Item Valuation", {"voucher_no": sr.name}))
|
repost_exists = bool(frappe.db.exists("Repost Item Valuation", {"voucher_no": sr.name}))
|
||||||
self.assertFalse(repost_exists, msg="Negative stock validation not working on reco cancellation")
|
self.assertFalse(repost_exists, msg="Negative stock validation not working on reco cancellation")
|
||||||
|
|
||||||
# teardown
|
|
||||||
frappe.db.rollback()
|
|
||||||
|
|
||||||
|
|
||||||
def test_valid_batch(self):
|
def test_valid_batch(self):
|
||||||
create_batch_item_with_batch("Testing Batch Item 1", "001")
|
create_batch_item_with_batch("Testing Batch Item 1", "001")
|
||||||
create_batch_item_with_batch("Testing Batch Item 2", "002")
|
create_batch_item_with_batch("Testing Batch Item 2", "002")
|
||||||
|
@ -7,9 +7,12 @@ import unittest
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
class TestStockSettings(unittest.TestCase):
|
|
||||||
|
class TestStockSettings(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
frappe.db.set_value("Stock Settings", None, "clean_description_html", 0)
|
frappe.db.set_value("Stock Settings", None, "clean_description_html", 0)
|
||||||
|
|
||||||
def test_settings(self):
|
def test_settings(self):
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.test_runner import make_test_records
|
from frappe.test_runner import make_test_records
|
||||||
from frappe.utils import cint
|
from frappe.utils import cint
|
||||||
@ -12,11 +10,13 @@ import erpnext
|
|||||||
from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
|
from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
|
||||||
from erpnext.stock.doctype.item.test_item import create_item
|
from erpnext.stock.doctype.item.test_item import create_item
|
||||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
test_records = frappe.get_test_records('Warehouse')
|
test_records = frappe.get_test_records('Warehouse')
|
||||||
|
|
||||||
class TestWarehouse(unittest.TestCase):
|
class TestWarehouse(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
if not frappe.get_value('Item', '_Test Item'):
|
if not frappe.get_value('Item', '_Test Item'):
|
||||||
make_test_records('Item')
|
make_test_records('Item')
|
||||||
|
|
||||||
|
@ -0,0 +1,97 @@
|
|||||||
|
{
|
||||||
|
"creation": "2021-07-29 12:32:08.929900",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Form Tour",
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": 1,
|
||||||
|
"modified": "2021-10-05 13:11:13.119453",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Stock",
|
||||||
|
"name": "Material Request",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_doctype": "Material Request",
|
||||||
|
"save_on_complete": 1,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"description": "The purpose of the material request can be selected here. For now select \"Purchase\" as the purpose.",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "material_request_type",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"has_next_condition": 1,
|
||||||
|
"is_table_field": 0,
|
||||||
|
"label": "Purpose",
|
||||||
|
"next_step_condition": "eval: doc.material_request_type == \"Purchase\"",
|
||||||
|
"parent_field": "",
|
||||||
|
"position": "Bottom",
|
||||||
|
"title": "Purpose"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Set the \"Required By\" date for the materials. This sets the \"Required By\" date for all the items.",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "schedule_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"has_next_condition": 0,
|
||||||
|
"is_table_field": 0,
|
||||||
|
"label": "Required By",
|
||||||
|
"next_step_condition": "",
|
||||||
|
"parent_field": "",
|
||||||
|
"position": "Left",
|
||||||
|
"title": "Required By"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Setting the target warehouse sets it for all the items.",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "set_warehouse",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"has_next_condition": 0,
|
||||||
|
"is_table_field": 0,
|
||||||
|
"label": "Set Target Warehouse",
|
||||||
|
"next_step_condition": "",
|
||||||
|
"parent_field": "",
|
||||||
|
"position": "Left",
|
||||||
|
"title": "Target Warehouse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Items table",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "items",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"has_next_condition": 0,
|
||||||
|
"is_table_field": 0,
|
||||||
|
"label": "Items",
|
||||||
|
"parent_field": "",
|
||||||
|
"position": "Bottom",
|
||||||
|
"title": "Items"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"child_doctype": "Material Request Item",
|
||||||
|
"description": "Select an Item code. Item details will be fetched automatically.",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "item_code",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"has_next_condition": 1,
|
||||||
|
"is_table_field": 1,
|
||||||
|
"label": "Item Code",
|
||||||
|
"next_step_condition": "eval: doc.item_code",
|
||||||
|
"parent_field": "",
|
||||||
|
"parent_fieldname": "items",
|
||||||
|
"position": "Right",
|
||||||
|
"title": "Item Code"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"child_doctype": "Material Request Item",
|
||||||
|
"description": "Enter the required quantity for the material.",
|
||||||
|
"field": "",
|
||||||
|
"fieldname": "qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"has_next_condition": 0,
|
||||||
|
"is_table_field": 1,
|
||||||
|
"label": "Quantity",
|
||||||
|
"parent_field": "",
|
||||||
|
"parent_fieldname": "items",
|
||||||
|
"position": "Bottom",
|
||||||
|
"title": "Quantity"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Material Request"
|
||||||
|
}
|
@ -5,9 +5,10 @@ from frappe import _dict
|
|||||||
|
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
from erpnext.stock.report.stock_analytics.stock_analytics import get_period_date_ranges
|
from erpnext.stock.report.stock_analytics.stock_analytics import get_period_date_ranges
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestStockAnalyticsReport(unittest.TestCase):
|
class TestStockAnalyticsReport(ERPNextTestCase):
|
||||||
def test_get_period_date_ranges(self):
|
def test_get_period_date_ranges(self):
|
||||||
|
|
||||||
filters = _dict(range="Monthly", from_date="2020-12-28", to_date="2021-02-06")
|
filters = _dict(range="Monthly", from_date="2020-12-28", to_date="2021-02-06")
|
||||||
|
@ -13,8 +13,8 @@ from six import iteritems
|
|||||||
|
|
||||||
import erpnext
|
import erpnext
|
||||||
from erpnext.stock.utils import (
|
from erpnext.stock.utils import (
|
||||||
get_bin,
|
|
||||||
get_incoming_outgoing_rate_for_cancel,
|
get_incoming_outgoing_rate_for_cancel,
|
||||||
|
get_or_make_bin,
|
||||||
get_valuation_method,
|
get_valuation_method,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -805,14 +805,13 @@ class update_entries_after(object):
|
|||||||
def update_bin(self):
|
def update_bin(self):
|
||||||
# update bin for each warehouse
|
# update bin for each warehouse
|
||||||
for warehouse, data in iteritems(self.data):
|
for warehouse, data in iteritems(self.data):
|
||||||
bin_doc = get_bin(self.item_code, warehouse)
|
bin_record = get_or_make_bin(self.item_code, warehouse)
|
||||||
bin_doc.update({
|
|
||||||
|
frappe.db.set_value('Bin', bin_record, {
|
||||||
"valuation_rate": data.valuation_rate,
|
"valuation_rate": data.valuation_rate,
|
||||||
"actual_qty": data.qty_after_transaction,
|
"actual_qty": data.qty_after_transaction,
|
||||||
"stock_value": data.stock_value
|
"stock_value": data.stock_value
|
||||||
})
|
})
|
||||||
bin_doc.flags.via_stock_ledger_entry = True
|
|
||||||
bin_doc.save(ignore_permissions=True)
|
|
||||||
|
|
||||||
|
|
||||||
def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False):
|
def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False):
|
||||||
@ -918,7 +917,7 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
|
|||||||
company = erpnext.get_default_company()
|
company = erpnext.get_default_company()
|
||||||
|
|
||||||
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
||||||
from `tabStock Ledger Entry`
|
from `tabStock Ledger Entry` force index (item_warehouse)
|
||||||
where
|
where
|
||||||
item_code = %s
|
item_code = %s
|
||||||
AND warehouse = %s
|
AND warehouse = %s
|
||||||
@ -929,7 +928,7 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
|
|||||||
if not last_valuation_rate:
|
if not last_valuation_rate:
|
||||||
# Get valuation rate from last sle for the item against any warehouse
|
# Get valuation rate from last sle for the item against any warehouse
|
||||||
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
||||||
from `tabStock Ledger Entry`
|
from `tabStock Ledger Entry` force index (item_code)
|
||||||
where
|
where
|
||||||
item_code = %s
|
item_code = %s
|
||||||
AND valuation_rate > 0
|
AND valuation_rate > 0
|
||||||
|
@ -180,12 +180,27 @@ def get_bin(item_code, warehouse):
|
|||||||
bin_obj.flags.ignore_permissions = True
|
bin_obj.flags.ignore_permissions = True
|
||||||
return bin_obj
|
return bin_obj
|
||||||
|
|
||||||
|
def get_or_make_bin(item_code, warehouse) -> str:
|
||||||
|
bin_record = frappe.db.get_value('Bin', {'item_code': item_code, 'warehouse': warehouse})
|
||||||
|
|
||||||
|
if not bin_record:
|
||||||
|
bin_obj = frappe.get_doc({
|
||||||
|
"doctype": "Bin",
|
||||||
|
"item_code": item_code,
|
||||||
|
"warehouse": warehouse,
|
||||||
|
})
|
||||||
|
bin_obj.flags.ignore_permissions = 1
|
||||||
|
bin_obj.insert()
|
||||||
|
bin_record = bin_obj.name
|
||||||
|
|
||||||
|
return bin_record
|
||||||
|
|
||||||
def update_bin(args, allow_negative_stock=False, via_landed_cost_voucher=False):
|
def update_bin(args, allow_negative_stock=False, via_landed_cost_voucher=False):
|
||||||
|
from erpnext.stock.doctype.bin.bin import update_stock
|
||||||
is_stock_item = frappe.get_cached_value('Item', args.get("item_code"), 'is_stock_item')
|
is_stock_item = frappe.get_cached_value('Item', args.get("item_code"), 'is_stock_item')
|
||||||
if is_stock_item:
|
if is_stock_item:
|
||||||
bin = get_bin(args.get("item_code"), args.get("warehouse"))
|
bin_record = get_or_make_bin(args.get("item_code"), args.get("warehouse"))
|
||||||
bin.update_stock(args, allow_negative_stock, via_landed_cost_voucher)
|
update_stock(bin_record, args, allow_negative_stock, via_landed_cost_voucher)
|
||||||
return bin
|
|
||||||
else:
|
else:
|
||||||
frappe.msgprint(_("Item {0} ignored since it is not a stock item").format(args.get("item_code")))
|
frappe.msgprint(_("Item {0} ignored since it is not a stock item").format(args.get("item_code")))
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import unittest
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from typing import Any, Dict, NewType, Optional
|
from typing import Any, Dict, NewType, Optional
|
||||||
|
|
||||||
@ -12,6 +13,21 @@ ReportFilters = Dict[str, Any]
|
|||||||
ReportName = NewType("ReportName", str)
|
ReportName = NewType("ReportName", str)
|
||||||
|
|
||||||
|
|
||||||
|
class ERPNextTestCase(unittest.TestCase):
|
||||||
|
"""A sane default test class for ERPNext tests."""
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls) -> None:
|
||||||
|
frappe.db.commit()
|
||||||
|
return super().setUpClass()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls) -> None:
|
||||||
|
frappe.db.rollback()
|
||||||
|
return super().tearDownClass()
|
||||||
|
|
||||||
|
|
||||||
def create_test_contact_and_address():
|
def create_test_contact_and_address():
|
||||||
frappe.db.sql('delete from tabContact')
|
frappe.db.sql('delete from tabContact')
|
||||||
frappe.db.sql('delete from `tabContact Email`')
|
frappe.db.sql('delete from `tabContact Email`')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user