Merge branch 'develop' into salary-structure-help-fix

This commit is contained in:
Anurag Mishra 2020-06-24 17:41:32 +05:30 committed by GitHub
commit 2f867ca357
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 1629 additions and 1197 deletions

View File

@ -2,10 +2,11 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day, cint, get_link_to_form
from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day, get_first_day, cint, get_link_to_form, rounded
from erpnext.accounts.utils import get_account_currency
from frappe.email import sendmail_to_system_managers
from frappe.utils.background_jobs import enqueue
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
def validate_service_stop_date(doc):
''' Validates service_stop_date for Purchase Invoice and Sales Invoice '''
@ -109,6 +110,18 @@ def get_booking_dates(doc, item, posting_date=None):
order by posting_date desc limit 1
''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
prev_gl_via_je = frappe.db.sql('''
SELECT p.name, p.posting_date FROM `tabJournal Entry` p, `tabJournal Entry Account` c
WHERE p.name = c.parent and p.company=%s and c.account=%s
and c.reference_type=%s and c.reference_name=%s
and c.reference_detail_no=%s and c.docstatus < 2 order by posting_date desc limit 1
''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
if prev_gl_via_je:
if (not prev_gl_entry) or (prev_gl_entry and
prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date):
prev_gl_entry = prev_gl_via_je
if prev_gl_entry:
start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1))
else:
@ -130,14 +143,48 @@ def get_booking_dates(doc, item, posting_date=None):
else:
return None, None, None
def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency):
if doc.doctype == "Sales Invoice":
total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency"
deferred_account = "deferred_revenue_account"
else:
total_credit_debit, total_credit_debit_currency = "credit", "credit_in_account_currency"
deferred_account = "deferred_expense_account"
def calculate_monthly_amount(doc, item, last_gl_entry, start_date, end_date, total_days, total_booking_days, account_currency):
amount, base_amount = 0, 0
if not last_gl_entry:
total_months = (item.service_end_date.year - item.service_start_date.year) * 12 + \
(item.service_end_date.month - item.service_start_date.month) + 1
prorate_factor = flt(date_diff(item.service_end_date, item.service_start_date)) \
/ flt(date_diff(get_last_day(item.service_end_date), get_first_day(item.service_start_date)))
actual_months = rounded(total_months * prorate_factor, 1)
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
base_amount = flt(item.base_net_amount / actual_months, item.precision("base_net_amount"))
if base_amount + already_booked_amount > item.base_net_amount:
base_amount = item.base_net_amount - already_booked_amount
if account_currency==doc.company_currency:
amount = base_amount
else:
amount = flt(item.net_amount/actual_months, item.precision("net_amount"))
if amount + already_booked_amount_in_account_currency > item.net_amount:
amount = item.net_amount - already_booked_amount_in_account_currency
if not (get_first_day(start_date) == start_date and get_last_day(end_date) == end_date):
partial_month = flt(date_diff(end_date, start_date)) \
/ flt(date_diff(get_last_day(end_date), get_first_day(start_date)))
base_amount = rounded(partial_month, 1) * base_amount
amount = rounded(partial_month, 1) * amount
else:
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
if account_currency==doc.company_currency:
amount = base_amount
else:
amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount"))
return amount, base_amount
def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency):
amount, base_amount = 0, 0
if not last_gl_entry:
base_amount = flt(item.base_net_amount*total_booking_days/flt(total_days), item.precision("base_net_amount"))
@ -146,27 +193,55 @@ def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, a
else:
amount = flt(item.net_amount*total_booking_days/flt(total_days), item.precision("net_amount"))
else:
gl_entries_details = frappe.db.sql('''
select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
group by voucher_detail_no
'''.format(total_credit_debit, total_credit_debit_currency),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
if account_currency==doc.company_currency:
amount = base_amount
else:
already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0
amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount"))
return amount, base_amount
def get_already_booked_amount(doc, item):
if doc.doctype == "Sales Invoice":
total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency"
deferred_account = "deferred_revenue_account"
else:
total_credit_debit, total_credit_debit_currency = "credit", "credit_in_account_currency"
deferred_account = "deferred_expense_account"
gl_entries_details = frappe.db.sql('''
select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
group by voucher_detail_no
'''.format(total_credit_debit, total_credit_debit_currency),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
journal_entry_details = frappe.db.sql('''
SELECT sum(c.{0}) as total_credit, sum(c.{1}) as total_credit_in_account_currency, reference_detail_no
FROM `tabJournal Entry` p , `tabJournal Entry Account` c WHERE p.name = c.parent and
p.company = %s and c.account=%s and c.reference_type=%s and c.reference_name=%s and c.reference_detail_no=%s
and p.docstatus < 2 group by reference_detail_no
'''.format(total_credit_debit, total_credit_debit_currency),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0
already_booked_amount += journal_entry_details[0].total_credit if journal_entry_details else 0
if doc.currency == doc.company_currency:
already_booked_amount_in_account_currency = already_booked_amount
else:
already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0
already_booked_amount_in_account_currency += journal_entry_details[0].total_credit_in_account_currency if journal_entry_details else 0
return already_booked_amount, already_booked_amount_in_account_currency
def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
enable_check = "enable_deferred_revenue" \
if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
def _book_deferred_revenue_or_expense(item):
def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on):
start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
if not (start_date and end_date): return
@ -181,23 +256,34 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
total_days = date_diff(item.service_end_date, item.service_start_date) + 1
total_booking_days = date_diff(end_date, start_date) + 1
amount, base_amount = calculate_amount(doc, item, last_gl_entry,
total_days, total_booking_days, account_currency)
if book_deferred_entries_based_on == 'Months':
amount, base_amount = calculate_monthly_amount(doc, item, last_gl_entry,
start_date, end_date, total_days, total_booking_days, account_currency)
else:
amount, base_amount = calculate_amount(doc, item, last_gl_entry,
total_days, total_booking_days, account_currency)
make_gl_entries(doc, credit_account, debit_account, against,
amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process)
if via_journal_entry:
book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount,
base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry)
else:
make_gl_entries(doc, credit_account, debit_account, against,
amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process)
# Returned in case of any errors because it tries to submit the same record again and again in case of errors
if frappe.flags.deferred_accounting_error:
return
if getdate(end_date) < getdate(posting_date) and not last_gl_entry:
_book_deferred_revenue_or_expense(item)
_book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on)
via_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_via_journal_entry'))
submit_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'submit_journal_entries'))
book_deferred_entries_based_on = frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_based_on')
for item in doc.get('items'):
if item.get(enable_check):
_book_deferred_revenue_or_expense(item)
_book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on)
def process_deferred_accounting(posting_date=None):
''' Converts deferred income/expense into income/expense
@ -281,3 +367,83 @@ def send_mail(deferred_process):
and submit manually after resolving errors
""").format(get_link_to_form('Process Deferred Accounting', deferred_process))
sendmail_to_system_managers(title, content)
def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
amount, base_amount, posting_date, project, account_currency, cost_center, item,
deferred_process=None, submit='No'):
if amount == 0: return
journal_entry = frappe.new_doc('Journal Entry')
journal_entry.posting_date = posting_date
journal_entry.company = doc.company
journal_entry.voucher_type = 'Deferred Revenue' if doc.doctype == 'Sales Invoice' \
else 'Deferred Expense'
debit_entry = {
'account': credit_account,
'credit': base_amount,
'credit_in_account_currency': amount,
'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier',
'party': against,
'account_currency': account_currency,
'reference_name': doc.name,
'reference_type': doc.doctype,
'reference_detail_no': item.name,
'cost_center': cost_center,
'project': project,
}
credit_entry = {
'account': debit_account,
'debit': base_amount,
'debit_in_account_currency': amount,
'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier',
'party': against,
'account_currency': account_currency,
'reference_name': doc.name,
'reference_type': doc.doctype,
'reference_detail_no': item.name,
'cost_center': cost_center,
'project': project,
}
for dimension in get_accounting_dimensions():
debit_entry.update({
dimension: item.get(dimension)
})
credit_entry.update({
dimension: item.get(dimension)
})
journal_entry.append('accounts', debit_entry)
journal_entry.append('accounts', credit_entry)
try:
journal_entry.save()
if submit:
journal_entry.submit()
except:
frappe.db.rollback()
traceback = frappe.get_traceback()
frappe.log_error(message=traceback)
frappe.flags.deferred_accounting_error = True
def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
if doctype == 'Sales Invoice':
credit_account, debit_account = frappe.db.get_value('Sales Invoice Item', {'name': voucher_detail_no},
['income_account', 'deferred_revenue_account'])
else:
credit_account, debit_account = frappe.db.get_value('Purchase Invoice Item', {'name': voucher_detail_no},
['deferred_expense_account', 'expense_account'])
if dr_or_cr == 'Debit':
return debit_account
else:
return credit_account

View File

@ -34,11 +34,15 @@
{
"fieldname": "properties",
"fieldtype": "Section Break",
"oldfieldtype": "Section Break"
"oldfieldtype": "Section Break",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "column_break0",
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1,
"width": "50%"
},
{
@ -49,7 +53,9 @@
"no_copy": 1,
"oldfieldname": "account_name",
"oldfieldtype": "Data",
"reqd": 1
"reqd": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "account_number",
@ -57,13 +63,17 @@
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Account Number",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"fieldname": "is_group",
"fieldtype": "Check",
"label": "Is Group"
"label": "Is Group",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "company",
@ -75,7 +85,9 @@
"options": "Company",
"read_only": 1,
"remember_last_selected_value": 1,
"reqd": 1
"reqd": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "root_type",
@ -83,7 +95,9 @@
"in_standard_filter": 1,
"label": "Root Type",
"options": "\nAsset\nLiability\nIncome\nExpense\nEquity",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "report_type",
@ -91,24 +105,32 @@
"in_standard_filter": 1,
"label": "Report Type",
"options": "\nBalance Sheet\nProfit and Loss",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"depends_on": "eval:doc.is_group==0",
"fieldname": "account_currency",
"fieldtype": "Link",
"label": "Currency",
"options": "Currency"
"options": "Currency",
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"fieldname": "inter_company_account",
"fieldtype": "Check",
"label": "Inter Company Account"
"label": "Inter Company Account",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "column_break1",
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1,
"width": "50%"
},
{
@ -120,7 +142,9 @@
"oldfieldtype": "Link",
"options": "Account",
"reqd": 1,
"search_index": 1
"search_index": 1,
"show_days": 1,
"show_seconds": 1
},
{
"description": "Setting Account Type helps in selecting this Account in transactions.",
@ -130,7 +154,9 @@
"label": "Account Type",
"oldfieldname": "account_type",
"oldfieldtype": "Select",
"options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nDepreciation\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nTax\nTemporary"
"options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nDepreciation\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary",
"show_days": 1,
"show_seconds": 1
},
{
"description": "Rate at which this tax is applied",
@ -138,7 +164,9 @@
"fieldtype": "Float",
"label": "Rate",
"oldfieldname": "tax_rate",
"oldfieldtype": "Currency"
"oldfieldtype": "Currency",
"show_days": 1,
"show_seconds": 1
},
{
"description": "If the account is frozen, entries are allowed to restricted users.",
@ -147,13 +175,17 @@
"label": "Frozen",
"oldfieldname": "freeze_account",
"oldfieldtype": "Select",
"options": "No\nYes"
"options": "No\nYes",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "balance_must_be",
"fieldtype": "Select",
"label": "Balance must be",
"options": "\nDebit\nCredit"
"options": "\nDebit\nCredit",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "lft",
@ -162,7 +194,9 @@
"label": "Lft",
"print_hide": 1,
"read_only": 1,
"search_index": 1
"search_index": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "rgt",
@ -171,7 +205,9 @@
"label": "Rgt",
"print_hide": 1,
"read_only": 1,
"search_index": 1
"search_index": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "old_parent",
@ -179,27 +215,33 @@
"hidden": 1,
"label": "Old Parent",
"print_hide": 1,
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"depends_on": "eval:(doc.report_type == 'Profit and Loss' && !doc.is_group)",
"fieldname": "include_in_gross",
"fieldtype": "Check",
"label": "Include in gross"
"label": "Include in gross",
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disable"
"label": "Disable",
"show_days": 1,
"show_seconds": 1
}
],
"icon": "fa fa-money",
"idx": 1,
"is_tree": 1,
"links": [],
"modified": "2020-03-18 17:57:52.063233",
"modified": "2020-06-11 15:15:54.338622",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account",

View File

@ -22,7 +22,12 @@
"allow_cost_center_in_entry_of_bs_account",
"add_taxes_from_item_tax_template",
"automatically_fetch_payment_terms",
"deferred_accounting_settings_section",
"automatically_process_deferred_accounting_entry",
"book_deferred_entries_based_on",
"column_break_18",
"book_deferred_entries_via_journal_entry",
"submit_journal_entries",
"print_settings",
"show_inclusive_tax_in_print",
"column_break_12",
@ -189,13 +194,45 @@
"fieldname": "automatically_process_deferred_accounting_entry",
"fieldtype": "Check",
"label": "Automatically Process Deferred Accounting Entry"
},
{
"fieldname": "deferred_accounting_settings_section",
"fieldtype": "Section Break",
"label": "Deferred Accounting Settings"
},
{
"fieldname": "column_break_18",
"fieldtype": "Column Break"
},
{
"default": "0",
"description": "If this is unchecked direct GL Entries will be created to book Deferred Revenue/Expense",
"fieldname": "book_deferred_entries_via_journal_entry",
"fieldtype": "Check",
"label": "Book Deferred Entries Via Journal Entry"
},
{
"default": "0",
"depends_on": "eval:doc.book_deferred_entries_via_journal_entry",
"description": "If this is unchecked Journal Entries will be saved in a Draft state and will have to be submitted manually",
"fieldname": "submit_journal_entries",
"fieldtype": "Check",
"label": "Submit Journal Entries"
},
{
"default": "Days",
"description": "If \"Months\" is selected then fixed amount will be booked as deferred revenue or expense for each month irrespective of number of days in a month. Will be prorated if deferred revenue or expense is not booked for an entire month.",
"fieldname": "book_deferred_entries_based_on",
"fieldtype": "Select",
"label": "Book Deferred Entries Based On",
"options": "Days\nMonths"
}
],
"icon": "icon-cog",
"idx": 1,
"issingle": 1,
"links": [],
"modified": "2019-12-19 16:58:17.395595",
"modified": "2020-06-22 20:13:26.043092",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@ -83,7 +83,7 @@
"label": "Entry Type",
"oldfieldname": "voucher_type",
"oldfieldtype": "Select",
"options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation",
"options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation\nDeferred Revenue\nDeferred Expense",
"reqd": 1,
"search_index": 1
},

View File

@ -10,6 +10,7 @@ from erpnext.accounts.utils import get_balance_on, get_account_currency
from erpnext.accounts.party import get_party_account
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting
from erpnext.accounts.deferred_revenue import get_deferred_booking_accounts
from six import string_types, iteritems
@ -265,7 +266,10 @@ class JournalEntry(AccountsController):
# set totals
if not d.reference_name in self.reference_totals:
self.reference_totals[d.reference_name] = 0.0
self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr))
if self.voucher_type not in ('Deferred Revenue', 'Deferred Expense'):
self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr))
self.reference_types[d.reference_name] = d.reference_type
self.reference_accounts[d.reference_name] = d.account
@ -277,10 +281,16 @@ class JournalEntry(AccountsController):
# check if party and account match
if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
if d.reference_type == "Sales Invoice":
party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
if self.voucher_type in ('Deferred Revenue', 'Deferred Expense') and d.reference_detail_no:
debit_or_credit = 'Debit' if d.debit else 'Credit'
party_account = get_deferred_booking_accounts(d.reference_type, d.reference_detail_no,
debit_or_credit)
else:
party_account = against_voucher[1]
if d.reference_type == "Sales Invoice":
party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
else:
party_account = against_voucher[1]
if (against_voucher[0] != d.party or party_account != d.account):
frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}")
.format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1],
@ -513,14 +523,20 @@ class JournalEntry(AccountsController):
"against_voucher_type": d.reference_type,
"against_voucher": d.reference_name,
"remarks": remarks,
"voucher_detail_no": d.reference_detail_no,
"cost_center": d.cost_center,
"project": d.project,
"finance_book": self.finance_book
}, item=d)
)
if self.voucher_type in ('Deferred Revenue', 'Deferred Expense'):
update_outstanding = 'No'
else:
update_outstanding = 'Yes'
if gl_map:
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj)
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding)
def get_balance(self):
if not self.get('accounts'):

View File

@ -33,6 +33,7 @@
"reference_type",
"reference_name",
"reference_due_date",
"reference_detail_no",
"col_break3",
"is_advance",
"user_remark",
@ -268,6 +269,12 @@
"fieldtype": "Link",
"label": "Bank Account",
"options": "Bank Account"
},
{
"fieldname": "reference_detail_no",
"fieldtype": "Data",
"hidden": 1,
"label": "Reference Detail No"
}
],
"idx": 1,

View File

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

View File

@ -10,6 +10,18 @@ frappe.ui.form.on('Process Deferred Accounting', {
}
};
});
if (frm.doc.company) {
frm.set_query("account", function() {
return {
filters: {
'company': frm.doc.company,
'root_type': 'Liability',
'is_group': 0
}
};
});
}
},
validate: function() {

View File

@ -238,6 +238,12 @@ class PurchaseInvoice(BuyingController):
not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")):
if self.update_stock and (not item.from_warehouse):
if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]:
frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because account {2}
is not linked to warehouse {3} or it is not the default inventory account'''.format(
item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]),
frappe.bold(item.expense_account), frappe.bold(item.warehouse))))
item.expense_account = warehouse_account[item.warehouse]["account"]
else:
# check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not
@ -247,10 +253,21 @@ class PurchaseInvoice(BuyingController):
(item.purchase_receipt, stock_not_billed_account))
if negative_expense_booked_in_pr:
if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because
expense is booked against this account in Purchase Receipt {2}'''.format(
item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.purchase_receipt))))
item.expense_account = stock_not_billed_account
else:
# If no purchase receipt present then book expense in 'Stock Received But Not Billed'
# This is done in cases when Purchase Invoice is created before Purchase Receipt
if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
frappe.msgprint(_('''Row {0}: Expense Head changed to {1} as no Purchase
Receipt is created against Item {2}. This is done to handle accounting for cases
when Purchase Receipt is created after Purchase Invoice'''.format(
item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.item_code))))
item.expense_account = stock_not_billed_account
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
@ -570,6 +587,20 @@ class PurchaseInvoice(BuyingController):
else:
amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount"))
auto_accounting_for_non_stock_items = cint(frappe.db.get_value('Company', self.company, 'enable_perpetual_inventory_for_non_stock_items'))
if auto_accounting_for_non_stock_items:
service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed")
if item.purchase_receipt:
# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
expense_booked_in_pr = frappe.db.get_value('GL Entry', {'is_cancelled': 0,
'voucher_type': 'Purchase Receipt', 'voucher_no': item.purchase_receipt, 'voucher_detail_no': item.pr_detail,
'account':service_received_but_not_billed_account}, ['name'])
if expense_booked_in_pr:
expense_account = service_received_but_not_billed_account
gl_entries.append(self.get_gl_dict({
"account": expense_account,
"against": self.supplier,
@ -1020,7 +1051,7 @@ class PurchaseInvoice(BuyingController):
# calculate totals again after applying TDS
self.calculate_taxes_and_totals()
def set_status(self, update=False, status=None, update_modified=True):
if self.is_new():
if self.get('amended_from'):

View File

@ -7,14 +7,15 @@ import unittest
import frappe, erpnext
import frappe.model
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from frappe.utils import cint, flt, today, nowdate, add_days
from frappe.utils import cint, flt, today, nowdate, add_days, getdate
import frappe.defaults
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
test_records as pr_test_records, make_purchase_receipt, get_taxes
from erpnext.controllers.accounts_controller import get_payment_terms
from erpnext.exceptions import InvalidCurrency
from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
from erpnext.stock.doctype.item.test_item import create_item
test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"]
test_ignore = ["Serial No"]
@ -866,6 +867,67 @@ class TestPurchaseInvoice(unittest.TestCase):
for gle in gl_entries:
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
def test_deferred_expense_via_journal_entry(self):
deferred_account = create_account(account_name="Deferred Expense",
parent_account="Current Assets - _TC", company="_Test Company")
acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
acc_settings.book_deferred_entries_via_journal_entry = 1
acc_settings.submit_journal_entries = 1
acc_settings.save()
item = create_item("_Test Item for Deferred Accounting")
item.enable_deferred_expense = 1
item.deferred_expense_account = deferred_account
item.save()
pi = make_purchase_invoice(item=item.name, qty=1, rate=100, do_not_save=True)
pi.set_posting_time = 1
pi.posting_date = '2019-03-15'
pi.items[0].enable_deferred_expense = 1
pi.items[0].service_start_date = "2019-01-10"
pi.items[0].service_end_date = "2019-03-15"
pi.items[0].deferred_expense_account = deferred_account
pi.save()
pi.submit()
pda1 = frappe.get_doc(dict(
doctype='Process Deferred Accounting',
posting_date=nowdate(),
start_date="2019-01-01",
end_date="2019-03-31",
type="Expense",
company="_Test Company"
))
pda1.insert()
pda1.submit()
expected_gle = [
["_Test Account Cost for Goods Sold - _TC", 0.0, 33.85, "2019-01-31"],
[deferred_account, 33.85, 0.0, "2019-01-31"],
["_Test Account Cost for Goods Sold - _TC", 0.0, 43.08, "2019-02-28"],
[deferred_account, 43.08, 0.0, "2019-02-28"],
["_Test Account Cost for Goods Sold - _TC", 0.0, 23.07, "2019-03-15"],
[deferred_account, 23.07, 0.0, "2019-03-15"]
]
gl_entries = gl_entries = frappe.db.sql("""select account, debit, credit, posting_date
from `tabGL Entry`
where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s
order by posting_date asc, account asc""", (pi.items[0].name, pi.posting_date), as_dict=1)
for i, gle in enumerate(gl_entries):
self.assertEqual(expected_gle[i][0], gle.account)
self.assertEqual(expected_gle[i][1], gle.credit)
self.assertEqual(expected_gle[i][2], gle.debit)
self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
acc_settings.book_deferred_entries_via_journal_entry = 0
acc_settings.submit_journal_entriessubmit_journal_entries = 0
acc_settings.save()
def unlink_payment_on_cancel_of_invoice(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings")

View File

@ -1720,8 +1720,6 @@ class TestSalesInvoice(unittest.TestCase):
si.save()
si.submit()
from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income
pda1 = frappe.get_doc(dict(
doctype='Process Deferred Accounting',
posting_date=nowdate(),
@ -1745,6 +1743,55 @@ class TestSalesInvoice(unittest.TestCase):
check_gl_entries(self, si.name, expected_gle, "2019-01-30")
def test_fixed_deferred_revenue(self):
deferred_account = create_account(account_name="Deferred Revenue",
parent_account="Current Liabilities - _TC", company="_Test Company")
acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
acc_settings.book_deferred_entries_based_on = 'Months'
acc_settings.save()
item = create_item("_Test Item for Deferred Accounting")
item.enable_deferred_revenue = 1
item.deferred_revenue_account = deferred_account
item.no_of_months = 12
item.save()
si = create_sales_invoice(item=item.name, posting_date="2019-01-16", rate=50000, do_not_submit=True)
si.items[0].enable_deferred_revenue = 1
si.items[0].service_start_date = "2019-01-16"
si.items[0].service_end_date = "2019-03-31"
si.items[0].deferred_revenue_account = deferred_account
si.save()
si.submit()
pda1 = frappe.get_doc(dict(
doctype='Process Deferred Accounting',
posting_date='2019-03-31',
start_date="2019-01-01",
end_date="2019-03-31",
type="Income",
company="_Test Company"
))
pda1.insert()
pda1.submit()
expected_gle = [
[deferred_account, 10000.0, 0.0, "2019-01-31"],
["Sales - _TC", 0.0, 10000.0, "2019-01-31"],
[deferred_account, 20000.0, 0.0, "2019-02-28"],
["Sales - _TC", 0.0, 20000.0, "2019-02-28"],
[deferred_account, 20000.0, 0.0, "2019-03-31"],
["Sales - _TC", 0.0, 20000.0, "2019-03-31"]
]
check_gl_entries(self, si.name, expected_gle, "2019-01-30")
acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
acc_settings.book_deferred_entries_based_on = 'Days'
acc_settings.save()
def test_inter_company_transaction(self):
if not frappe.db.exists("Customer", "_Test Internal Customer"):

View File

@ -20,6 +20,7 @@ from erpnext.exceptions import InvalidCurrency
from six import text_type
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
from erpnext.stock.get_item_details import get_item_warehouse
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
@ -1301,6 +1302,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
parent.set_qty_as_per_stock_uom()
parent.calculate_taxes_and_totals()
if parent_doctype == "Sales Order":
make_packing_list(parent)
parent.set_gross_profit()
frappe.get_doc('Authorization Control').validate_approving_authority(parent.doctype,
parent.company, parent.base_grand_total)

View File

@ -29,6 +29,9 @@
"requires_fulfilment",
"fulfilment_deadline",
"fulfilment_terms",
"authorised_by_section",
"signee_company",
"signed_by_company",
"sb_references",
"document_type",
"cb_links",
@ -223,10 +226,28 @@
"options": "Contract",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "signee_company",
"fieldtype": "Signature",
"label": "Signee (Company)"
},
{
"fieldname": "signed_by_company",
"fieldtype": "Link",
"label": "Signed By (Company)",
"options": "User",
"read_only": 1
},
{
"fieldname": "authorised_by_section",
"fieldtype": "Section Break",
"label": "Authorised By"
}
],
"is_submittable": 1,
"modified": "2019-09-30 00:56:41.559681",
"links": [],
"modified": "2020-03-30 06:56:07.257932",
"modified_by": "Administrator",
"module": "CRM",
"name": "Contract",

View File

@ -29,6 +29,9 @@ class Contract(Document):
self.update_contract_status()
self.update_fulfilment_status()
def before_submit(self):
self.signed_by_company = frappe.session.user
def before_update_after_submit(self):
self.update_contract_status()
self.update_fulfilment_status()

View File

@ -1,306 +1,106 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:title",
"beta": 0,
"creation": "2018-04-16 06:44:48.791312",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"allow_rename": 1,
"autoname": "field:title",
"creation": "2018-04-16 06:44:48.791312",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"title",
"contract_terms",
"sb_fulfilment",
"requires_fulfilment",
"fulfilment_terms"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "title",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Title",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "title",
"fieldtype": "Data",
"label": "Title",
"unique": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sb_terms",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "contract_terms",
"fieldtype": "Text Editor",
"label": "Contract Terms and Conditions",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "contract_terms",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Contract Terms and Conditions",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "sb_fulfilment",
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sb_fulfilment",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"default": "0",
"fieldname": "requires_fulfilment",
"fieldtype": "Check",
"label": "Requires Fulfilment"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "requires_fulfilment",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Requires Fulfilment",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.requires_fulfilment==1",
"fieldname": "fulfilment_terms",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Fulfilment Terms and Conditions",
"length": 0,
"no_copy": 0,
"options": "Contract Template Fulfilment Terms",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
"depends_on": "eval:doc.requires_fulfilment==1",
"fieldname": "fulfilment_terms",
"fieldtype": "Table",
"label": "Fulfilment Terms and Conditions",
"options": "Contract Template Fulfilment Terms"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-04-17 07:36:05.217599",
"modified_by": "Administrator",
"module": "CRM",
"name": "Contract Template",
"name_case": "",
"owner": "Administrator",
],
"links": [],
"modified": "2020-06-03 00:24:58.179816",
"modified_by": "Administrator",
"module": "CRM",
"name": "Contract Template",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
},
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase Manager",
"share": 1,
"write": 1
},
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

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

View File

@ -1,20 +1,21 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2013-05-13 14:04:03",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"is_standard": "Yes",
"modified": "2017-11-10 19:42:36.457449",
"modified_by": "Administrator",
"module": "Education",
"name": "Absent Student Report",
"owner": "Administrator",
"ref_doctype": "Student Attendance",
"report_name": "Absent Student Report",
"report_type": "Script Report",
"add_total_row": 0,
"creation": "2013-05-13 14:04:03",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"is_standard": "Yes",
"modified": "2020-06-24 17:16:40.251116",
"modified_by": "Administrator",
"module": "Education",
"name": "Absent Student Report",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Student Attendance",
"report_name": "Absent Student Report",
"report_type": "Script Report",
"roles": [
{
"role": "Academics User"

View File

@ -1,20 +1,21 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2017-11-09 15:07:30.404428",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2017-11-28 18:35:44.903665",
"modified_by": "Administrator",
"module": "Education",
"name": "Assessment Plan Status",
"owner": "Administrator",
"ref_doctype": "Assessment Plan",
"report_name": "Assessment Plan Status",
"report_type": "Script Report",
"add_total_row": 0,
"creation": "2017-11-09 15:07:30.404428",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2020-06-24 17:16:02.027410",
"modified_by": "Administrator",
"module": "Education",
"name": "Assessment Plan Status",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Assessment Plan",
"report_name": "Assessment Plan Status",
"report_type": "Script Report",
"roles": [
{
"role": "Academics User"

View File

@ -1,24 +1,26 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2017-05-05 14:46:13.776133",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2018-02-08 15:11:24.904628",
"modified_by": "Administrator",
"module": "Education",
"name": "Course wise Assessment Report",
"owner": "Administrator",
"ref_doctype": "Assessment Result",
"report_name": "Course wise Assessment Report",
"report_type": "Script Report",
"add_total_row": 0,
"creation": "2017-05-05 14:46:13.776133",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2020-06-24 17:15:15.477530",
"modified_by": "Administrator",
"module": "Education",
"name": "Course wise Assessment Report",
"owner": "Administrator",
"prepared_report": 0,
"query": "",
"ref_doctype": "Assessment Result",
"report_name": "Course wise Assessment Report",
"report_type": "Script Report",
"roles": [
{
"role": "Instructor"
},
},
{
"role": "Education Manager"
}

View File

@ -1,24 +1,25 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2018-01-22 17:04:43.412054",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2019-02-08 15:11:35.339434",
"modified_by": "Administrator",
"module": "Education",
"name": "Final Assessment Grades",
"owner": "Administrator",
"ref_doctype": "Assessment Result",
"report_name": "Final Assessment Grades",
"report_type": "Script Report",
"add_total_row": 0,
"creation": "2018-01-22 17:04:43.412054",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2020-06-24 17:13:35.373756",
"modified_by": "Administrator",
"module": "Education",
"name": "Final Assessment Grades",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Assessment Result",
"report_name": "Final Assessment Grades",
"report_type": "Script Report",
"roles": [
{
"role": "Instructor"
},
},
{
"role": "Education Manager"
}

View File

@ -1,24 +1,25 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2017-03-27 17:47:16.831433",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2017-11-10 19:42:30.300729",
"modified_by": "Administrator",
"module": "Education",
"name": "Student and Guardian Contact Details",
"owner": "Administrator",
"ref_doctype": "Program Enrollment",
"report_name": "Student and Guardian Contact Details",
"report_type": "Script Report",
"add_total_row": 0,
"creation": "2017-03-27 17:47:16.831433",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2020-06-24 17:16:50.639488",
"modified_by": "Administrator",
"module": "Education",
"name": "Student and Guardian Contact Details",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Program Enrollment",
"report_name": "Student and Guardian Contact Details",
"report_type": "Script Report",
"roles": [
{
"role": "Instructor"
},
},
{
"role": "Academics User"
}

View File

@ -1,20 +1,21 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2016-11-28 22:07:03.859124",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 2,
"is_standard": "Yes",
"modified": "2017-11-10 19:41:12.328346",
"modified_by": "Administrator",
"module": "Education",
"name": "Student Batch-Wise Attendance",
"owner": "Administrator",
"ref_doctype": "Student Attendance",
"report_name": "Student Batch-Wise Attendance",
"report_type": "Script Report",
"add_total_row": 0,
"creation": "2016-11-28 22:07:03.859124",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 2,
"is_standard": "Yes",
"modified": "2020-06-24 17:16:59.823709",
"modified_by": "Administrator",
"module": "Education",
"name": "Student Batch-Wise Attendance",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Student Attendance",
"report_name": "Student Batch-Wise Attendance",
"report_type": "Script Report",
"roles": [
{
"role": "Academics User"

View File

@ -1,21 +1,22 @@
{
"add_total_row": 0,
"creation": "2016-06-22 02:58:41.024538",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"is_standard": "Yes",
"modified": "2018-12-17 16:46:46.176620",
"modified_by": "Administrator",
"module": "Education",
"name": "Student Fee Collection",
"owner": "Administrator",
"prepared_report": 0,
"query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(grand_total) - sum(outstanding_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nGROUP BY\n student",
"ref_doctype": "Fees",
"report_name": "Student Fee Collection",
"report_type": "Query Report",
"add_total_row": 0,
"creation": "2016-06-22 02:58:41.024538",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"is_standard": "Yes",
"modified": "2020-06-24 17:14:39.452551",
"modified_by": "Administrator",
"module": "Education",
"name": "Student Fee Collection",
"owner": "Administrator",
"prepared_report": 0,
"query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(grand_total) - sum(outstanding_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nGROUP BY\n student",
"ref_doctype": "Fees",
"report_name": "Student Fee Collection",
"report_type": "Query Report",
"roles": [
{
"role": "Academics User"

View File

@ -1,20 +1,21 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2013-05-13 14:04:03",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"is_standard": "Yes",
"modified": "2017-11-10 19:42:43.376658",
"modified_by": "Administrator",
"module": "Education",
"name": "Student Monthly Attendance Sheet",
"owner": "Administrator",
"ref_doctype": "Student Attendance",
"report_name": "Student Monthly Attendance Sheet",
"report_type": "Script Report",
"add_total_row": 0,
"creation": "2013-05-13 14:04:03",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"is_standard": "Yes",
"modified": "2020-06-24 17:16:13.307053",
"modified_by": "Administrator",
"module": "Education",
"name": "Student Monthly Attendance Sheet",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Student Attendance",
"report_name": "Student Monthly Attendance Sheet",
"report_type": "Script Report",
"roles": [
{
"role": "Academics User"

View File

@ -22,6 +22,7 @@ class EmployeeAdvance(Document):
self.validate_employee_advance_account()
def on_cancel(self):
self.ignore_linked_doctypes = ('GL Entry')
self.set_status()
def set_status(self):

View File

@ -20,10 +20,9 @@ class JobOffer(Document):
staffing_plan = get_staffing_plan_detail(self.designation, self.company, self.offer_date)
check_vacancies = frappe.get_single("HR Settings").check_vacancies
if staffing_plan and check_vacancies:
vacancies = frappe.db.get_value("Staffing Plan Detail", filters={"name": staffing_plan.name}, fieldname=['vacancies'])
job_offers = len(self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date))
if vacancies - job_offers <= 0:
frappe.throw(_("There are no vacancies under staffing plan {0}").format(get_link_to_form("Staffing Plan", staffing_plan.parent)))
job_offers = self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date)
if staffing_plan.vacancies - len(job_offers) <= 0:
frappe.throw(_("There are no vacancies under staffing plan {0}").format(frappe.bold(get_link_to_form("Staffing Plan", staffing_plan.parent))))
def on_change(self):
update_job_applicant(self.status, self.job_applicant)
@ -42,18 +41,22 @@ def update_job_applicant(status, job_applicant):
def get_staffing_plan_detail(designation, company, offer_date):
detail = frappe.db.sql("""
SELECT spd.name as name,
SELECT DISTINCT spd.parent,
sp.from_date as from_date,
sp.to_date as to_date,
sp.name as parent
sp.name,
sum(spd.vacancies) as vacancies,
spd.designation
FROM `tabStaffing Plan Detail` spd, `tabStaffing Plan` sp
WHERE
sp.docstatus=1
AND spd.designation=%s
AND sp.company=%s
AND spd.parent = sp.name
AND %s between sp.from_date and sp.to_date
""", (designation, company, offer_date), as_dict=1)
return detail[0] if detail else None
return frappe._dict(detail[0]) if detail else None
@frappe.whitelist()
def make_employee(source_name, target_doc=None):

View File

@ -699,6 +699,7 @@ erpnext.patches.v13_0.delete_old_purchase_reports
erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions
erpnext.patches.v13_0.update_sla_enhancements
erpnext.patches.v12_0.update_address_template_for_india
erpnext.patches.v13_0.update_deferred_settings
erpnext.patches.v12_0.set_multi_uom_in_rfq
erpnext.patches.v13_0.delete_old_sales_reports
execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation")

View File

@ -0,0 +1,11 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
accounts_settings.book_deferred_entries_based_on = 'Days'
accounts_settings.book_deferred_entries_via_journal_entry = 0
accounts_settings.submit_journal_entries = 0
accounts_settings.save()

View File

@ -63,7 +63,7 @@
"description": "The fraction of daily wages to be paid for half-day attendance",
"fieldname": "daily_wages_fraction_for_half_day",
"fieldtype": "Float",
"label": "Daily Wages Fraction for Half Day",
"label": "Fraction of Daily Salary for Half Day",
"show_days": 1,
"show_seconds": 1
},
@ -109,7 +109,7 @@
"icon": "fa fa-cog",
"issingle": 1,
"links": [],
"modified": "2020-06-22 12:35:34.861674",
"modified": "2020-06-22 17:00:58.408030",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Payroll Settings",

View File

@ -8,7 +8,7 @@
Variables from Employee:<br> <code>Employment Type = employment_type</code>, <code>Branch = branch</code> etc.
</li>
<li>
Variables Salary Slip:<br>
Variables from Salary Slip:<br>
<code>Payment Days = payment_days</code>, <code>Leave without pay = leave_without_pay</code> etc.
</li>
<li>

View File

@ -14,6 +14,7 @@
],
"fields": [
{
"default": "0",
"fieldname": "from_amount",
"fieldtype": "Currency",
"in_list_view": 1,
@ -27,6 +28,7 @@
"label": "To Amount"
},
{
"default": "0",
"fieldname": "percent_deduction",
"fieldtype": "Percent",
"in_list_view": 1,
@ -51,7 +53,7 @@
],
"istable": 1,
"links": [],
"modified": "2020-06-22 23:32:47.253106",
"modified": "2020-06-22 18:16:07.596493",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Taxable Salary Slab",

View File

@ -86,6 +86,7 @@ def get_conditions(filters):
if filters.get("period"):
conditions.append("month(start_date) = '%s' " % (filters["period"]))
conditions.append("year(start_date) = '%s' " % (frappe.utils.getdate().year))
return " and ".join(conditions)

View File

@ -335,6 +335,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
var tax_rate = this._get_tax_rate(tax, item_tax_map);
var current_tax_amount = 0.0;
// To set row_id by default as previous row.
if(["On Previous Row Amount", "On Previous Row Total"].includes(tax.charge_type)) {
if (tax.idx === 1) {
frappe.throw(
__("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"));
}
if (!tax.row_id) {
tax.row_id = tax.idx - 1;
}
}
if(tax.charge_type == "Actual") {
// distribute the tax amount proportionally to each item row
var actual = flt(tax.tax_amount, precision("tax_amount", tax));

View File

@ -7,7 +7,7 @@ from __future__ import unicode_literals
import frappe
test_records = frappe.get_test_records('Product Bundle')
def make_product_bundle(parent, items):
def make_product_bundle(parent, items, qty=None):
if frappe.db.exists("Product Bundle", parent):
return frappe.get_doc("Product Bundle", parent)
@ -17,7 +17,7 @@ def make_product_bundle(parent, items):
})
for item in items:
product_bundle.append("items", {"item_code": item, "qty": 1})
product_bundle.append("items", {"item_code": item, "qty": qty or 1})
product_bundle.insert()

View File

@ -99,6 +99,8 @@ class Quotation(SellingController):
self.update_lead()
def on_cancel(self):
if self.lost_reasons:
self.lost_reasons = []
super(Quotation, self).on_cancel()
#update enquiry status

View File

@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
import json
from frappe.utils import flt, add_days, nowdate
import frappe.permissions
import unittest
@ -10,9 +11,10 @@ from erpnext.selling.doctype.sales_order.sales_order \
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.selling.doctype.sales_order.sales_order import make_work_orders
from erpnext.controllers.accounts_controller import update_child_qty_rate
import json
from erpnext.selling.doctype.sales_order.sales_order import make_raw_material_request
from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
from erpnext.stock.doctype.item.test_item import make_item
class TestSalesOrder(unittest.TestCase):
def tearDown(self):
@ -417,6 +419,26 @@ class TestSalesOrder(unittest.TestCase):
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
frappe.set_user("Administrator")
def test_update_child_qty_rate_product_bundle(self):
# test Update Items with product bundle
if not frappe.db.exists("Item", "_Product Bundle Item"):
bundle_item = make_item("_Product Bundle Item", {"is_stock_item": 0})
bundle_item.append("item_defaults", {
"company": "_Test Company",
"default_warehouse": "_Test Warehouse - _TC"})
bundle_item.save(ignore_permissions=True)
make_item("_Packed Item", {"is_stock_item": 1})
make_product_bundle("_Product Bundle Item", ["_Packed Item"], 2)
so = make_sales_order(item_code = "_Test Item", warehouse=None)
added_item = json.dumps([{"item_code" : "_Product Bundle Item", "rate" : 200, 'qty' : 2}])
update_child_qty_rate('Sales Order', added_item, so.name)
so.reload()
self.assertEqual(so.packed_items[0].qty, 4)
def test_warehouse_user(self):
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com")
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
@ -457,8 +479,6 @@ class TestSalesOrder(unittest.TestCase):
self.assertRaises(frappe.CancelledLinkError, dn.submit)
def test_service_type_product_bundle(self):
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
from erpnext.stock.doctype.item.test_item import make_item
make_item("_Test Service Product Bundle", {"is_stock_item": 0})
make_item("_Test Service Product Bundle Item 1", {"is_stock_item": 0})
make_item("_Test Service Product Bundle Item 2", {"is_stock_item": 0})
@ -472,8 +492,6 @@ class TestSalesOrder(unittest.TestCase):
self.assertTrue("_Test Service Product Bundle Item 2" in [d.item_code for d in so.packed_items])
def test_mix_type_product_bundle(self):
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
from erpnext.stock.doctype.item.test_item import make_item
make_item("_Test Mix Product Bundle", {"is_stock_item": 0})
make_item("_Test Mix Product Bundle Item 1", {"is_stock_item": 1})
make_item("_Test Mix Product Bundle Item 2", {"is_stock_item": 0})
@ -484,7 +502,6 @@ class TestSalesOrder(unittest.TestCase):
self.assertRaises(WarehouseRequired, make_sales_order, item_code = "_Test Mix Product Bundle", warehouse="")
def test_auto_insert_price(self):
from erpnext.stock.doctype.item.test_item import make_item
make_item("_Test Item for Auto Price List", {"is_stock_item": 0})
frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1)
@ -519,7 +536,6 @@ class TestSalesOrder(unittest.TestCase):
from erpnext.buying.doctype.purchase_order.purchase_order import update_status
make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
from erpnext.stock.doctype.item.test_item import make_item
po_item = make_item("_Test Item for Drop Shipping", {"is_stock_item": 1, "delivered_by_supplier": 1})
dn_item = make_item("_Test Regular Item", {"is_stock_item": 1})
@ -714,7 +730,6 @@ class TestSalesOrder(unittest.TestCase):
def test_serial_no_based_delivery(self):
frappe.set_value("Stock Settings", None, "automatically_set_serial_nos_based_on_fifo", 1)
from erpnext.stock.doctype.item.test_item import make_item
item = make_item("_Reserved_Serialized_Item", {"is_stock_item": 1,
"maintain_stock": 1,
"has_serial_no": 1,
@ -835,7 +850,6 @@ class TestSalesOrder(unittest.TestCase):
self.assertRaises(frappe.LinkExistsError, so_doc.cancel)
def test_request_for_raw_materials(self):
from erpnext.stock.doctype.item.test_item import make_item
item = make_item("_Test Finished Item", {"is_stock_item": 1,
"maintain_stock": 1,
"valuation_rate": 500,

View File

@ -9,6 +9,9 @@ from frappe.utils.nestedset import get_descendants_of
def execute(filters=None):
filters = frappe._dict(filters or {})
if filters.from_date > filters.to_date:
frappe.throw(_('From Date cannot be greater than To Date'))
columns = get_columns(filters)
data = get_data(filters)

View File

@ -264,7 +264,10 @@ erpnext.company.setup_queries = function(frm) {
["expenses_included_in_valuation",
{"root_type": "Expense", "account_type": "Expenses Included in Valuation"}],
["stock_received_but_not_billed",
{"root_type": "Liability", "account_type": "Stock Received But Not Billed"}]
{"root_type": "Liability", "account_type": "Stock Received But Not Billed"}],
["service_received_but_not_billed",
{"root_type": "Liability", "account_type": "Service Received But Not Billed"}],
], function(i, v) {
erpnext.company.set_custom_query(frm, v);
});

View File

@ -67,10 +67,12 @@
"payment_terms",
"auto_accounting_for_stock_settings",
"enable_perpetual_inventory",
"enable_perpetual_inventory_for_non_stock_items",
"default_inventory_account",
"stock_adjustment_account",
"column_break_32",
"stock_received_but_not_billed",
"service_received_but_not_billed",
"expenses_included_in_valuation",
"fixed_asset_depreciation_settings",
"accumulated_depreciation_account",
@ -723,6 +725,20 @@
"fieldtype": "Link",
"label": "Default Buying Terms",
"options": "Terms and Conditions"
},
{
"fieldname": "service_received_but_not_billed",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Service Received But Not Billed",
"no_copy": 1,
"options": "Account"
},
{
"default": "0",
"fieldname": "enable_perpetual_inventory_for_non_stock_items",
"fieldtype": "Check",
"label": "Enable Perpetual Inventory For Non Stock Items"
}
],
"icon": "fa fa-building",
@ -730,7 +746,7 @@
"image_field": "company_logo",
"is_tree": 1,
"links": [],
"modified": "2020-03-21 18:09:53.534211",
"modified": "2020-06-20 11:38:43.178970",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",

View File

@ -46,6 +46,7 @@ class Company(NestedSet):
self.validate_currency()
self.validate_coa_input()
self.validate_perpetual_inventory()
self.validate_perpetual_inventory_for_non_stock_items()
self.check_country_change()
self.set_chart_of_accounts()
self.validate_parent_company()
@ -182,6 +183,12 @@ class Company(NestedSet):
frappe.msgprint(_("Set default inventory account for perpetual inventory"),
alert=True, indicator='orange')
def validate_perpetual_inventory_for_non_stock_items(self):
if not self.get("__islocal"):
if cint(self.enable_perpetual_inventory_for_non_stock_items) == 1 and not self.service_received_but_not_billed:
frappe.throw(_("Set default {0} account for perpetual inventory for non stock items").format(
frappe.bold('Service Received But Not Billed')))
def check_country_change(self):
frappe.flags.country_change = False

View File

@ -8,7 +8,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) {
cur_frm.cscript.set_root_readonly = function(doc) {
// read-only for root customer group
if(!doc.parent_customer_group) {
if(!doc.parent_customer_group && !doc.__islocal) {
cur_frm.set_read_only();
cur_frm.set_intro(__("This is a root customer group and cannot be edited."));
} else {
@ -20,7 +20,8 @@ cur_frm.cscript.set_root_readonly = function(doc) {
cur_frm.fields_dict['parent_customer_group'].get_query = function(doc,cdt,cdn) {
return {
filters: {
'is_group': 1
'is_group': 1,
'name': ['!=', cur_frm.doc.customer_group_name]
}
}
}

View File

@ -66,7 +66,7 @@ frappe.ui.form.on("Item Group", {
set_root_readonly: function(frm) {
// read-only for root item group
frm.set_intro("");
if(!frm.doc.parent_item_group) {
if(!frm.doc.parent_item_group && !frm.doc.__islocal) {
frm.set_read_only();
frm.set_intro(__("This is a root item group and cannot be edited."), true);
}

View File

@ -19,7 +19,7 @@ frappe.ui.form.on('Sales Person', {
}
}
};
frm.make_methods = {
'Sales Order': () => frappe.new_doc("Sales Order")
.then(() => frm.add_child("sales_team", {"sales_person": frm.doc.name}))
@ -33,7 +33,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) {
cur_frm.cscript.set_root_readonly = function(doc) {
// read-only for root
if(!doc.parent_sales_person) {
if(!doc.parent_sales_person && !doc.__islocal) {
cur_frm.set_read_only();
cur_frm.set_intro(__("This is a root sales person and cannot be edited."));
} else {

View File

@ -8,7 +8,7 @@ cur_frm.cscript.refresh = function(doc) {
cur_frm.cscript.set_root_readonly = function(doc) {
// read-only for root customer group
if(!doc.parent_supplier_group) {
if(!doc.parent_supplier_group && !doc.__islocal) {
cur_frm.set_read_only();
cur_frm.set_intro(__("This is a root supplier group and cannot be edited."));
} else {
@ -20,7 +20,8 @@ cur_frm.cscript.set_root_readonly = function(doc) {
cur_frm.fields_dict['parent_supplier_group'].get_query = function() {
return {
filters: {
'is_group': 1
'is_group': 1,
'name': ['!=', cur_frm.doc.supplier_group_name]
}
};
};

View File

@ -20,7 +20,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) {
cur_frm.cscript.set_root_readonly = function(doc) {
// read-only for root territory
if(!doc.parent_territory) {
if(!doc.parent_territory && !doc.__islocal) {
cur_frm.set_read_only();
cur_frm.set_intro(__("This is a root territory and cannot be edited."));
} else {

View File

@ -597,7 +597,7 @@ class Item(WebsiteGenerator):
def stock_ledger_created(self):
if not hasattr(self, '_stock_ledger_created'):
self._stock_ledger_created = len(frappe.db.sql("""select name from `tabStock Ledger Entry`
where item_code = %s limit 1""", self.name))
where item_code = %s and is_cancelled = 0 limit 1""", self.name))
return self._stock_ledger_created
def validate_name_with_item_group(self):
@ -883,7 +883,12 @@ class Item(WebsiteGenerator):
linked_doctypes += ["Sales Order Item", "Purchase Order Item", "Material Request Item"]
for doctype in linked_doctypes:
if frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}) or \
if doctype in ("Purchase Invoice Item", "Sales Invoice Item",):
# If Invoice has Stock impact, only then consider it.
if self.stock_ledger_created():
return True
elif frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}) or \
frappe.db.get_value("Production Order",
filters={"production_item": self.name, "docstatus": 1}):
return True

View File

@ -211,6 +211,7 @@ class PurchaseReceipt(BuyingController):
stock_rbnb = self.get_company_default("stock_received_but_not_billed")
landed_cost_entries = get_item_account_wise_additional_cost(self.name)
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
auto_accounting_for_non_stock_items = cint(frappe.db.get_value('Company', self.company, 'enable_perpetual_inventory_for_non_stock_items'))
gl_entries = []
warehouse_with_no_account = []
@ -301,6 +302,32 @@ class PurchaseReceipt(BuyingController):
elif d.warehouse not in warehouse_with_no_account or \
d.rejected_warehouse not in warehouse_with_no_account:
warehouse_with_no_account.append(d.warehouse)
elif d.item_code not in stock_items and flt(d.qty) and auto_accounting_for_non_stock_items:
service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed")
credit_currency = get_account_currency(service_received_but_not_billed_account)
gl_entries.append(self.get_gl_dict({
"account": service_received_but_not_billed_account,
"against": d.expense_account,
"cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Service"),
"project": d.project,
"credit": d.amount,
"voucher_detail_no": d.name
}, credit_currency, item=d))
debit_currency = get_account_currency(d.expense_account)
gl_entries.append(self.get_gl_dict({
"account": d.expense_account,
"against": service_received_but_not_billed_account,
"cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Service"),
"project": d.project,
"debit": d.amount,
"voucher_detail_no": d.name
}, debit_currency, item=d))
self.get_asset_gl_entry(gl_entries)
# Cost center-wise amount breakup for other charges included for valuation

View File

@ -574,9 +574,7 @@ class StockEntry(StockController):
{"parent": self.purchase_order, "item_code": se_item.subcontracted_item},
"bom")
allow_alternative_item = frappe.get_value("BOM", bom_no, "allow_alternative_item")
if allow_alternative_item:
if se_item.allow_alternative_item:
original_item_code = frappe.get_value("Item Alternative", {"alternative_item_code": item_code}, "item_code")
required_qty = sum([flt(d.required_qty) for d in purchase_order.supplied_items \
@ -743,7 +741,7 @@ class StockEntry(StockController):
def get_item_details(self, args=None, for_update=False):
item = frappe.db.sql("""select i.name, i.stock_uom, i.description, i.image, i.item_name, i.item_group,
i.has_batch_no, i.sample_quantity, i.has_serial_no,
i.has_batch_no, i.sample_quantity, i.has_serial_no, i.allow_alternative_item,
id.expense_account, id.buying_cost_center
from `tabItem` i LEFT JOIN `tabItem Default` id ON i.name=id.parent and id.company=%s
where i.name=%s
@ -778,6 +776,9 @@ class StockEntry(StockController):
'expense_account' : item.expense_account
})
if self.purpose == 'Send to Subcontractor':
ret["allow_alternative_item"] = item.allow_alternative_item
# update uom
if args.get("uom") and for_update:
ret.update(get_uom_details(args.get('item_code'), args.get('uom'), args.get('qty')))

View File

@ -216,7 +216,7 @@
"idx": 1,
"issingle": 1,
"links": [],
"modified": "2020-04-01 18:11:25.417678",
"modified": "2020-06-20 11:39:15.344112",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Settings",

View File

@ -12,8 +12,9 @@
} %}
{% set link = '' %}
{% set label = domains[0].domain %}
{% set label = '' %}
{% if domains %}
{% set label = domains[0].domain %}
{% set link = links[label] %}
{% endif %}

File diff suppressed because it is too large Load Diff

View File

@ -161,19 +161,19 @@ class TransactionBase(StatusUpdater):
if not (self.get("update_stock") or self.get("is_pos")):
return
fiscal_year = get_fiscal_year(self.get('posting_date'), as_dict=True).name
for item in self.get('items'):
last_transaction_time = frappe.db.sql("""
select MAX(timestamp(posting_date, posting_time)) as posting_time
from `tabStock Ledger Entry`
where docstatus = 1 and item_code = %s """, (item.item_code))[0][0]
last_transaction_time = frappe.db.sql("""
select MAX(timestamp(posting_date, posting_time)) as posting_time
from `tabStock Ledger Entry`
where docstatus = 1""")[0][0]
cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time):
frappe.throw(_("""Posting timestamp of current transaction
must be after last Stock transaction's timestamp which is {0}""").format(frappe.bold(last_transaction_time)),
title=_("Backdated Stock Entry"))
if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time):
msg = _("Last Stock Transaction for item {0} was on {1}.").format(frappe.bold(item.item_code), frappe.bold(last_transaction_time))
msg += "<br><br>" + _("Stock Transactions for Item {0} cannot be posted before this time.").format(frappe.bold(item.item_code))
msg += "<br><br>" + _("Please remove this item and try to submit again or update the posting time.")
frappe.throw(msg, title=_("Backdated Stock Entry"))
def delete_events(ref_type, ref_name):
events = frappe.db.sql_list(""" SELECT

View File

@ -9,6 +9,33 @@
<p class="hero-subtitle">{{ greeting_subtitle }}</p>
{% endif %}
</div>
<div class="search-container">
<div class="website-search" id="search-container">
<div class="dropdown">
<div class="search-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"
fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-search">
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
</div>
<input type="search" class="form-control" placeholder="Search the docs (Press ? to focus)" />
<div class="overflow-hidden shadow dropdown-menu w-100">
</div>
</div>
</div>
<button class="navbar-toggler" type="button"
data-toggle="collapse"
data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</div>
</section>
@ -54,5 +81,21 @@
</div>
</section>
{% endif %}
{% endblock %}
{% endblock %}
{%- block script -%}
<script>
frappe.ready(() => {
frappe.setup_search('#search-container', 'kb');
});
</script>
{%- endblock -%}
{%- block style -%}
<style>
.search-container {
margin-top: 1.2rem;
max-width: 500px;
}
</style>
{%- endblock -%}