Merge branch 'develop' into grn_posting

This commit is contained in:
rohitwaghchaure 2020-06-24 10:23:23 +05:30 committed by GitHub
commit d63c59ced4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
80 changed files with 1433 additions and 1164 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

@ -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

@ -71,8 +71,13 @@ frappe.ui.form.on('Cost Center', {
"label": "Cost Center Number",
"fieldname": "cost_center_number",
"fieldtype": "Data",
"reqd": 1,
"default": frm.doc.cost_center_number
},
{
"label": __("Merge with existing"),
"fieldname": "merge",
"fieldtype": "Check",
"default": 0
}
],
primary_action: function() {
@ -87,8 +92,9 @@ frappe.ui.form.on('Cost Center', {
args: {
docname: frm.doc.name,
cost_center_name: data.cost_center_name,
cost_center_number: data.cost_center_number,
company: frm.doc.company
cost_center_number: cstr(data.cost_center_number),
company: frm.doc.company,
merge: data.merge
},
callback: function(r) {
frappe.dom.unfreeze();

View File

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

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):

View File

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

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

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

View File

@ -63,7 +63,7 @@ def make_sales_invoice():
debit_to = 'Debtors - _TC2',
income_account = 'Sales - _TC2',
expense_account = 'Cost of Goods Sold - _TC2',
cost_center = '_Test Company 2 - _TC2',
cost_center = 'Main - _TC2',
do_not_save=1)
si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30))
@ -83,14 +83,14 @@ def make_payment(docname):
def make_credit_note(docname):
create_sales_invoice(company="_Test Company 2",
customer = '_Test Customer 2',
currency = 'EUR',
qty = -1,
warehouse = 'Finished Goods - _TC2',
debit_to = 'Debtors - _TC2',
income_account = 'Sales - _TC2',
expense_account = 'Cost of Goods Sold - _TC2',
cost_center = '_Test Company 2 - _TC2',
is_return = 1,
return_against = docname)
customer = '_Test Customer 2',
currency = 'EUR',
qty = -1,
warehouse = 'Finished Goods - _TC2',
debit_to = 'Debtors - _TC2',
income_account = 'Sales - _TC2',
expense_account = 'Cost of Goods Sold - _TC2',
cost_center = 'Main - _TC2',
is_return = 1,
return_against = docname)

View File

@ -837,7 +837,7 @@ def create_payment_gateway_account(gateway):
pass
@frappe.whitelist()
def update_cost_center(docname, cost_center_name, cost_center_number, company):
def update_cost_center(docname, cost_center_name, cost_center_number, company, merge):
'''
Renames the document by adding the number as a prefix to the current name and updates
all transaction where it was present.
@ -853,7 +853,7 @@ def update_cost_center(docname, cost_center_name, cost_center_number, company):
new_name = get_autoname_with_number(cost_center_number, cost_center_name, docname, company)
if docname != new_name:
frappe.rename_doc("Cost Center", docname, new_name, force=1)
frappe.rename_doc("Cost Center", docname, new_name, force=1, merge=merge)
return new_name
def validate_field_number(doctype_name, docname, number_value, company, field_name):

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

@ -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,9 +699,10 @@ 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")
erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll
erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings
erpnext.patches.v13_0.check_is_income_tax_component
erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll #22-06-2020
erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings #22-06-2020
erpnext.patches.v13_0.check_is_income_tax_component #22-06-2020

View File

@ -4,9 +4,28 @@
from __future__ import unicode_literals
import frappe, erpnext
from erpnext.regional.india.setup import setup
def execute():
frappe.reload_doc('Payroll', 'doctype', 'salary_structure')
doctypes = ['salary_component',
'Employee Tax Exemption Declaration',
'Employee Tax Exemption Proof Submission',
'Employee Tax Exemption Declaration Category',
'Employee Tax Exemption Proof Submission Detail'
]
for doctype in doctypes:
frappe.reload_doc('Payroll', 'doctype', doctype)
reports = ['Professional Tax Deductions', 'Provident Fund Deductions']
for report in reports:
frappe.reload_doc('Regional', 'Report', report)
frappe.reload_doc('Regional', 'Report', report)
if erpnext.get_region() == "India":
setup(patch=True)
if frappe.db.exists("Salary Component", "Income Tax"):
frappe.db.set_value("Salary Component", "Income Tax", "is_income_tax_component", 1)

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

@ -163,7 +163,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2020-05-27 21:10:50.374063",
"modified": "2020-06-22 21:10:50.374063",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Additional Salary",

View File

@ -119,7 +119,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2020-05-27 22:58:31.271922",
"modified": "2020-06-22 22:58:31.271922",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Employee Benefit Application",

View File

@ -45,7 +45,7 @@
],
"istable": 1,
"links": [],
"modified": "2020-05-27 23:45:00.519134",
"modified": "2020-06-22 23:45:00.519134",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Employee Benefit Application Detail",

View File

@ -123,7 +123,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2020-05-27 23:01:50.791676",
"modified": "2020-06-22 23:01:50.791676",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Employee Benefit Claim",

View File

@ -74,7 +74,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2020-05-27 22:42:51.209630",
"modified": "2020-06-22 22:42:51.209630",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Employee Incentive",

View File

@ -76,7 +76,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2020-05-27 22:55:17.604688",
"modified": "2020-06-22 22:55:17.604688",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Employee Other Income",

View File

@ -26,7 +26,7 @@
}
],
"links": [],
"modified": "2020-05-27 23:16:47.472910",
"modified": "2020-06-22 23:16:47.472910",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Employee Tax Exemption Category",

View File

@ -107,7 +107,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2020-05-27 22:49:43.829892",
"modified": "2020-06-22 22:49:43.829892",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Employee Tax Exemption Declaration",

View File

@ -48,7 +48,7 @@
],
"istable": 1,
"links": [],
"modified": "2020-05-27 23:41:03.638739",
"modified": "2020-06-22 23:41:03.638739",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Employee Tax Exemption Declaration Category",

View File

@ -130,7 +130,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2020-05-27 22:53:10.412321",
"modified": "2020-06-22 22:53:10.412321",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Employee Tax Exemption Proof Submission",

View File

@ -53,7 +53,7 @@
],
"istable": 1,
"links": [],
"modified": "2020-05-27 23:37:08.265600",
"modified": "2020-06-22 23:37:08.265600",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Employee Tax Exemption Proof Submission Detail",

View File

@ -38,7 +38,7 @@
}
],
"links": [],
"modified": "2020-05-27 23:18:08.254645",
"modified": "2020-06-22 23:18:08.254645",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Employee Tax Exemption Sub Category",

View File

@ -94,7 +94,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2020-05-27 20:27:13.425084",
"modified": "2020-06-22 20:27:13.425084",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Income Tax Slab",

View File

@ -62,7 +62,7 @@
],
"istable": 1,
"links": [],
"modified": "2020-05-27 23:33:17.931912",
"modified": "2020-06-22 23:33:17.931912",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Income Tax Slab Other Charges",

View File

@ -52,7 +52,7 @@
],
"istable": 1,
"links": [],
"modified": "2020-05-27 23:25:13.779032",
"modified": "2020-06-22 23:25:13.779032",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Payroll Employee Detail",

View File

@ -262,7 +262,7 @@
"icon": "fa fa-cog",
"is_submittable": 1,
"links": [],
"modified": "2020-05-27 20:06:06.953904",
"modified": "2020-06-22 20:06:06.953904",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Payroll Entry",

View File

@ -53,7 +53,7 @@
}
],
"links": [],
"modified": "2020-05-27 20:12:32.684189",
"modified": "2020-06-22 20:12:32.684189",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Payroll Period",

View File

@ -26,7 +26,7 @@
],
"istable": 1,
"links": [],
"modified": "2020-05-27 23:30:15.943356",
"modified": "2020-06-22 23:30:15.943356",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Payroll Period Date",

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-05 12:35:34.861674",
"modified": "2020-06-22 17:00:58.408030",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Payroll Settings",

View File

@ -93,7 +93,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2020-05-27 22:42:05.251951",
"modified": "2020-06-22 22:42:05.251951",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Retention Bonus",

View File

@ -245,7 +245,7 @@
],
"icon": "fa fa-flag",
"links": [],
"modified": "2020-06-01 15:39:20.826565",
"modified": "2020-06-22 15:39:20.826565",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Component",

View File

@ -211,7 +211,7 @@
],
"istable": 1,
"links": [],
"modified": "2020-05-27 23:21:26.300951",
"modified": "2020-06-22 23:21:26.300951",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Detail",

View File

@ -616,7 +616,7 @@
"idx": 9,
"is_submittable": 1,
"links": [],
"modified": "2020-06-05 14:42:43.921828",
"modified": "2020-06-22 14:42:43.921828",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Slip",

View File

@ -28,7 +28,7 @@
],
"istable": 1,
"links": [],
"modified": "2020-05-27 23:27:43.463532",
"modified": "2020-06-22 23:27:43.463532",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Slip Timesheet",

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

@ -282,7 +282,7 @@
"idx": 1,
"is_submittable": 1,
"links": [],
"modified": "2020-06-05 17:07:26.129355",
"modified": "2020-06-22 17:07:26.129355",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Structure",

View File

@ -124,7 +124,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2020-05-27 19:58:09.964692",
"modified": "2020-06-22 19:58:09.964692",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Structure Assignment",

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-05-27 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

@ -1,5 +1,5 @@
{
"add_total_row": 0,
"add_total_row": 1,
"creation": "2018-09-21 12:46:29.451048",
"disable_prepared_report": 0,
"disabled": 0,
@ -7,7 +7,7 @@
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2020-04-30 19:49:02.303320",
"modified": "2020-06-19 17:41:03.132101",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Analytics",

View File

@ -23,7 +23,14 @@ class Analytics(object):
self.get_columns()
self.get_data()
self.get_chart_data()
return self.columns, self.data, None, self.chart
# Skipping total row for tree-view reports
skip_total_row = 0
if self.filters.tree_type in ["Supplier Group", "Item Group", "Customer Group", "Territory"]:
skip_total_row = 1
return self.columns, self.data, None, self.chart, None, skip_total_row
def get_columns(self):
self.columns = [{
@ -194,9 +201,6 @@ class Analytics(object):
def get_rows(self):
self.data = []
self.get_periodic_data()
total_row = {
"entity": "Total",
}
for entity, period_data in iteritems(self.entity_periodic_data):
row = {
@ -210,9 +214,6 @@ class Analytics(object):
row[scrub(period)] = amount
total += amount
if not total_row.get(scrub(period)): total_row[scrub(period)] = 0
total_row[scrub(period)] += amount
row["total"] = total
if self.filters.tree_type == "Item":
@ -220,8 +221,6 @@ class Analytics(object):
self.data.append(row)
self.data.append(total_row)
def get_rows_by_group(self):
self.get_periodic_data()
out = []

View File

@ -33,21 +33,6 @@ class TestAnalytics(unittest.TestCase):
report = execute(filters)
expected_data = [
{
'entity': 'Total',
'apr_2017': 0.0,
'may_2017': 0.0,
'jun_2017': 2000.0,
'jul_2017': 1000.0,
'aug_2017': 0.0,
'sep_2017': 1500.0,
'oct_2017': 1000.0,
'nov_2017': 0.0,
'dec_2017': 0.0,
'jan_2018': 0.0,
'feb_2018': 2000.0,
'mar_2018': 0.0
},
{
"entity": "_Test Customer 1",
"entity_name": "_Test Customer 1",
@ -149,21 +134,6 @@ class TestAnalytics(unittest.TestCase):
report = execute(filters)
expected_data = [
{
'entity': 'Total',
'apr_2017': 0.0,
'may_2017': 0.0,
'jun_2017': 20.0,
'jul_2017': 10.0,
'aug_2017': 0.0,
'sep_2017': 15.0,
'oct_2017': 10.0,
'nov_2017': 0.0,
'dec_2017': 0.0,
'jan_2018': 0.0,
'feb_2018': 20.0,
'mar_2018': 0.0
},
{
"entity": "_Test Customer 1",
"entity_name": "_Test Customer 1",

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

@ -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

@ -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

@ -59,7 +59,7 @@
{% macro title() %}
<div class="mb-3">
<a href="/lms/course?name={{ course }}&program={{ program }}" class="text-muted">
Back to Course
{{_('Back to Course')}}
</a>
</div>
<div>
@ -69,15 +69,15 @@
{% macro navigation() %}
{% if previous %}
<a href="/lms/content?program={{ program }}&course={{ course }}&topic={{ topic }}&type={{ previous.content_type }}&content={{ previous.content }}" class='btn text-muted' style="box-shadow: none;">Previous</a>
<a href="/lms/content?program={{ program }}&course={{ course }}&topic={{ topic }}&type={{ previous.content_type }}&content={{ previous.content }}" class='btn text-muted' style="box-shadow: none;">{{_('Previous')}}</a>
{% else %}
<a href="/lms/course?name={{ course }}&program={{ program }}" class='btn text-muted' style="box-shadow: none;">Back to Course</a>
<a href="/lms/course?name={{ course }}&program={{ program }}" class='btn text-muted' style="box-shadow: none;">{{ _('Back to Course') }}</a>
{% endif %}
{% if next %}
<button id="nextButton" onclick="handle('/lms/content?program={{ program }}&course={{ course }}&topic={{ topic }}&type={{ next.content_type }}&content={{ next.content }}')" class='btn btn-primary' disabled="true">Next</button>
<button id="nextButton" onclick="handle('/lms/content?program={{ program }}&course={{ course }}&topic={{ topic }}&type={{ next.content_type }}&content={{ next.content }}')" class='btn btn-primary' disabled="true">{{_('Next')}}</button>
{% else %}
<button id="nextButton" onclick="handle('/lms/course?name={{ course }}&program={{ program }}')" class='btn btn-primary' disabled="true">Finish Topic</button>
<button id="nextButton" onclick="handle('/lms/course?name={{ course }}&program={{ program }}')" class='btn btn-primary' disabled="true">{{_('Finish Topic')}}</button>
{% endif %}
{% endmacro %}
@ -86,7 +86,7 @@
{{ title() }}
<div class="text-muted">
{% if content.duration %}
{{ content.duration }} Mins
{{ content.duration }} {{_('Mins')}}
{% endif %}
{% if content.publish_date and content.duration%}
@ -94,7 +94,7 @@
{% endif %}
{% if content.publish_date %}
Published on {{ content.publish_date.strftime('%d, %b %Y') }}
{{_('Published on')}} {{ content.publish_date.strftime('%d, %b %Y') }}
{% endif %}
</div>
</div>
@ -109,13 +109,13 @@
{{ title() }}
<div class="text-muted">
{% if content.author or content.publish_date %}
Published
{{_('Published')}}
{% endif %}
{% if content.author %}
by {{ content.author }}
{{_('by')}} {{ content.author }}
{% endif %}
{% if content.publish_date %}
on {{ content.publish_date.strftime('%d, %b %Y') }}
{{_('on')}} {{ content.publish_date.strftime('%d, %b %Y') }}
{% endif %}
</div>
</div>
@ -205,4 +205,4 @@
{% endif %}
</script>
{% endblock %}
{% endblock %}

View File

@ -72,11 +72,11 @@
{% if has_access %}
<div class='card-footer'>
{% if progress[topic.name].completed %}
<span class="indicator green">Completed</span>
<span class="indicator green">{{_('Completed')}}</span>
{% elif progress[topic.name].started %}
<span class="indicator orange">In Progress</span>
<span class="indicator orange">{{_('In Progress')}}</span>
{% else %}
<span class="indicator blue">Start</span>
<span class="indicator blue">{{_('Start')}}</span>
{% endif %}
</div>
</a>

View File

@ -45,7 +45,7 @@
<p class='lead'>{{ education_settings.description }}</p>
<p class="mt-4">
{% if frappe.session.user == 'Guest' %}
<a class="btn btn-primary btn-lg" href="'/login#signup'">Sign Up</a>
<a class="btn btn-primary btn-lg" href="'/login#signup'">{{_('Sign Up')}}</a>
{% endif %}
</p>
</div>

View File

@ -15,8 +15,8 @@
</div>
{% if has_access or program.intro_video%}
<div class='card-footer'>
{% if has_access %} <span class="indicator green">Enrolled</span>
{% elif program.intro_video %} <span><a href="{{ program.intro_video }}" target="blank">Watch Intro</a></span>
{% if has_access %} <span class="indicator green">{{_('Enrolled')}}</span>
{% elif program.intro_video %} <span><a href="{{ program.intro_video }}" target="blank">{{_('Watch Intro')}}</a></span>
{% endif %}
</div>
{% endif %}

View File

@ -2,16 +2,16 @@
<div class='container pb-5'>
<div class="mb-3">
<a href="{{ back.url }}" class="text-muted">
Back to {{ back.name }}
{{_('Back to')}} {{ _(back.name) }}
</a>
</div>
<h1>{{ title }}</h1>
<p class='lead' style="max-width: 100%;">{{ description or ''}}</p>
<p class="mt-4">
{% if frappe.session.user == 'Guest' %}
<a id="signup" class="btn btn-primary btn-lg" href="/login#signup">Sign Up</a>
<a id="signup" class="btn btn-primary btn-lg" href="/login#signup">{{_('Sign Up')}}</a>
{% elif not has_access %}
<button id="enroll" class="btn btn-primary btn-lg" onclick="enroll()" disabled>Enroll</button>
<button id="enroll" class="btn btn-primary btn-lg" onclick="enroll()" disabled>{{_('Enroll')}}</button>
{% endif %}
</p>
</div>
@ -28,7 +28,7 @@
let btn = document.getElementById('enroll');
btn.disbaled = true;
btn.innerText = 'Enrolling...'
btn.innerText = __('Enrolling...')
let opts = {
method: 'erpnext.education.utils.enroll_in_program',
@ -44,7 +44,7 @@
window.location.reload()
}
})
success_dialog.set_message('You have successfully enrolled for the program ');
success_dialog.set_message(__('You have successfully enrolled for the program '));
success_dialog.$message.show()
success_dialog.show();
btn.disbaled = false;

View File

@ -30,7 +30,7 @@
</ul>
</div>
<div class='card-footer'>
<span class="small">{{ program.completion }}% Complete</span>
<span class="small">{{ program.completion }}{{_('% Complete')}}</span>
</div>
</div>
</a>
@ -43,11 +43,11 @@
<div class="mb-3 row">
<div class="col-md-7">
<a href="/lms" class="text-muted">
Back to Home
{{_('Back to Home')}}
</a>
</div>
<div class="col-md-5 text-right">
<a href="/update-profile?name={{ frappe.session.user }}" target="blank" class="mt-0 text-muted">Edit Profile</a>
<a href="/update-profile?name={{ frappe.session.user }}" target="blank" class="mt-0 text-muted">{{_('Edit Profile')}}</a>
</div>
</div>
<h1>{{ student.first_name }} {{ student.last_name or '' }}</h1>
@ -61,4 +61,4 @@
</div>
</div>
</section>
{% endblock %}
{% endblock %}

View File

@ -55,11 +55,11 @@
{% if has_access and progress[course.name] %}
<div class='card-footer'>
{% if progress[course.name].completed %}
<span class="indicator green">Completed</span>
<span class="indicator green">{{_('Completed')}}</span>
{% elif progress[course.name].started %}
<span class="indicator orange">In Progress</span>
<span class="indicator orange">{{_('In Progress')}}</span>
{% else %}
<span class="indicator blue">Start</span>
<span class="indicator blue">{{_('Start')}}</span>
{% endif %}
</div>
{% endif %}

View File

@ -23,13 +23,13 @@
{% if has_access %}
<div class='card-footer'>
{% if content.content_type == 'Quiz' %}
{% if content.result == 'Fail' %} <span class="indicator red">Fail <span class="text-muted">({{ content.score }}/100)</span></span>
{% elif content.result == 'Pass' %} <span class="indicator green">Pass <span class="text-muted">({{ content.score }}/100)</span>
{% else %} <span class="indicator blue">Start</span>
{% if content.result == 'Fail' %} <span class="indicator red">{{_('Fail')}} <span class="text-muted">({{ content.score }}/100)</span></span>
{% elif content.result == 'Pass' %} <span class="indicator green">{{_('Pass')}} <span class="text-muted">({{ content.score }}/100)</span>
{% else %} <span class="indicator blue">{{_('Start')}}</span>
{% endif %}
{% else %}
{% if content.completed %} <span class="indicator green">Completed</span>
{% else %} <span class="indicator blue">Start</span>
{% if content.completed %} <span class="indicator green">{{_('Completed')}}</span>
{% else %} <span class="indicator blue">{{_('Start')}}</span>
{% endif %}
{% endif %}
</div>