Resolve merge conflicts

This commit is contained in:
Deepesh Garg 2020-06-24 18:47:58 +05:30
commit be9a8e1a14
539 changed files with 11093 additions and 8628 deletions

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

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

View File

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

View File

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

View File

@ -2,10 +2,11 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ 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 erpnext.accounts.utils import get_account_currency
from frappe.email import sendmail_to_system_managers from frappe.email import sendmail_to_system_managers
from frappe.utils.background_jobs import enqueue 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): def validate_service_stop_date(doc):
''' Validates service_stop_date for Purchase Invoice and Sales Invoice ''' ''' 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 order by posting_date desc limit 1
''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) ''', (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: if prev_gl_entry:
start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1)) start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1))
else: else:
@ -130,14 +143,48 @@ def get_booking_dates(doc, item, posting_date=None):
else: else:
return None, None, None return None, None, None
def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency): def calculate_monthly_amount(doc, item, last_gl_entry, start_date, end_date, total_days, total_booking_days, account_currency):
if doc.doctype == "Sales Invoice": amount, base_amount = 0, 0
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"
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 amount, base_amount = 0, 0
if not last_gl_entry: if not last_gl_entry:
base_amount = flt(item.base_net_amount*total_booking_days/flt(total_days), item.precision("base_net_amount")) 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: else:
amount = flt(item.net_amount*total_booking_days/flt(total_days), item.precision("net_amount")) amount = flt(item.net_amount*total_booking_days/flt(total_days), item.precision("net_amount"))
else: else:
gl_entries_details = frappe.db.sql(''' already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
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
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount")) base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
if account_currency==doc.company_currency: if account_currency==doc.company_currency:
amount = base_amount amount = base_amount
else: 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")) amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount"))
return amount, base_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): def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
enable_check = "enable_deferred_revenue" \ enable_check = "enable_deferred_revenue" \
if doc.doctype=="Sales Invoice" else "enable_deferred_expense" 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) start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
if not (start_date and end_date): return 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_days = date_diff(item.service_end_date, item.service_start_date) + 1
total_booking_days = date_diff(end_date, start_date) + 1 total_booking_days = date_diff(end_date, start_date) + 1
amount, base_amount = calculate_amount(doc, item, last_gl_entry, if book_deferred_entries_based_on == 'Months':
total_days, total_booking_days, account_currency) 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, if via_journal_entry:
amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process) 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 # 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: if frappe.flags.deferred_accounting_error:
return return
if getdate(end_date) < getdate(posting_date) and not last_gl_entry: 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'): for item in doc.get('items'):
if item.get(enable_check): 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): def process_deferred_accounting(posting_date=None):
''' Converts deferred income/expense into income/expense ''' Converts deferred income/expense into income/expense
@ -281,3 +367,83 @@ def send_mail(deferred_process):
and submit manually after resolving errors and submit manually after resolving errors
""").format(get_link_to_form('Process Deferred Accounting', deferred_process)) """).format(get_link_to_form('Process Deferred Accounting', deferred_process))
sendmail_to_system_managers(title, content) 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

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

View File

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

View File

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

View File

@ -22,7 +22,12 @@
"allow_cost_center_in_entry_of_bs_account", "allow_cost_center_in_entry_of_bs_account",
"add_taxes_from_item_tax_template", "add_taxes_from_item_tax_template",
"automatically_fetch_payment_terms", "automatically_fetch_payment_terms",
"deferred_accounting_settings_section",
"automatically_process_deferred_accounting_entry", "automatically_process_deferred_accounting_entry",
"book_deferred_entries_based_on",
"column_break_18",
"book_deferred_entries_via_journal_entry",
"submit_journal_entries",
"print_settings", "print_settings",
"show_inclusive_tax_in_print", "show_inclusive_tax_in_print",
"column_break_12", "column_break_12",
@ -189,13 +194,45 @@
"fieldname": "automatically_process_deferred_accounting_entry", "fieldname": "automatically_process_deferred_accounting_entry",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Automatically Process Deferred Accounting Entry" "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", "icon": "icon-cog",
"idx": 1, "idx": 1,
"issingle": 1, "issingle": 1,
"links": [], "links": [],
"modified": "2019-12-19 16:58:17.395595", "modified": "2020-06-22 20:13:26.043092",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounts Settings", "name": "Accounts Settings",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class DistributedCostCenter(Document):
pass

View File

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

View File

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

View File

@ -83,7 +83,7 @@
"label": "Entry Type", "label": "Entry Type",
"oldfieldname": "voucher_type", "oldfieldname": "voucher_type",
"oldfieldtype": "Select", "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, "reqd": 1,
"search_index": 1 "search_index": 1
}, },
@ -191,6 +191,7 @@
{ {
"fieldname": "total_debit", "fieldname": "total_debit",
"fieldtype": "Currency", "fieldtype": "Currency",
"in_list_view": 1,
"label": "Total Debit", "label": "Total Debit",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "total_debit", "oldfieldname": "total_debit",
@ -252,7 +253,6 @@
"fieldname": "total_amount", "fieldname": "total_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 1, "hidden": 1,
"in_list_view": 1,
"label": "Total Amount", "label": "Total Amount",
"no_copy": 1, "no_copy": 1,
"options": "total_amount_currency", "options": "total_amount_currency",
@ -503,7 +503,7 @@
"idx": 176, "idx": 176,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-04-29 10:55:28.240916", "modified": "2020-06-02 18:15:46.955697",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry", "name": "Journal Entry",

View File

@ -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.accounts.party import get_party_account
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount 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.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 from six import string_types, iteritems
@ -54,7 +55,7 @@ class JournalEntry(AccountsController):
def on_cancel(self): def on_cancel(self):
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip from erpnext.payroll.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
unlink_ref_doc_from_payment_entries(self) unlink_ref_doc_from_payment_entries(self)
unlink_ref_doc_from_salary_slip(self.name) unlink_ref_doc_from_salary_slip(self.name)
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
@ -265,7 +266,10 @@ class JournalEntry(AccountsController):
# set totals # set totals
if not d.reference_name in self.reference_totals: if not d.reference_name in self.reference_totals:
self.reference_totals[d.reference_name] = 0.0 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_types[d.reference_name] = d.reference_type
self.reference_accounts[d.reference_name] = d.account self.reference_accounts[d.reference_name] = d.account
@ -277,10 +281,16 @@ class JournalEntry(AccountsController):
# check if party and account match # check if party and account match
if d.reference_type in ("Sales Invoice", "Purchase Invoice"): if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
if d.reference_type == "Sales Invoice": if self.voucher_type in ('Deferred Revenue', 'Deferred Expense') and d.reference_detail_no:
party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1] 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: 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): 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}") 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], .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_type": d.reference_type,
"against_voucher": d.reference_name, "against_voucher": d.reference_name,
"remarks": remarks, "remarks": remarks,
"voucher_detail_no": d.reference_detail_no,
"cost_center": d.cost_center, "cost_center": d.cost_center,
"project": d.project, "project": d.project,
"finance_book": self.finance_book "finance_book": self.finance_book
}, item=d) }, item=d)
) )
if self.voucher_type in ('Deferred Revenue', 'Deferred Expense'):
update_outstanding = 'No'
else:
update_outstanding = 'Yes'
if gl_map: 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): def get_balance(self):
if not self.get('accounts'): if not self.get('accounts'):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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() { 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")): not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")):
if self.update_stock and (not item.from_warehouse): if self.update_stock and (not item.from_warehouse):
if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]:
frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because account {2}
is not linked to warehouse {3} or it is not the default inventory account'''.format(
item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]),
frappe.bold(item.expense_account), frappe.bold(item.warehouse))))
item.expense_account = warehouse_account[item.warehouse]["account"] item.expense_account = warehouse_account[item.warehouse]["account"]
else: else:
# check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not # check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not
@ -247,10 +253,21 @@ class PurchaseInvoice(BuyingController):
(item.purchase_receipt, stock_not_billed_account)) (item.purchase_receipt, stock_not_billed_account))
if negative_expense_booked_in_pr: if negative_expense_booked_in_pr:
if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because
expense is booked against this account in Purchase Receipt {2}'''.format(
item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.purchase_receipt))))
item.expense_account = stock_not_billed_account item.expense_account = stock_not_billed_account
else: else:
# If no purchase receipt present then book expense in 'Stock Received But Not Billed' # If no purchase receipt present then book expense in 'Stock Received But Not Billed'
# This is done in cases when Purchase Invoice is created before Purchase Receipt # This is done in cases when Purchase Invoice is created before Purchase Receipt
if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
frappe.msgprint(_('''Row {0}: Expense Head changed to {1} as no Purchase
Receipt is created against Item {2}. This is done to handle accounting for cases
when Purchase Receipt is created after Purchase Invoice'''.format(
item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.item_code))))
item.expense_account = stock_not_billed_account item.expense_account = stock_not_billed_account
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category): elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
@ -1020,7 +1037,7 @@ class PurchaseInvoice(BuyingController):
# calculate totals again after applying TDS # calculate totals again after applying TDS
self.calculate_taxes_and_totals() self.calculate_taxes_and_totals()
def set_status(self, update=False, status=None, update_modified=True): def set_status(self, update=False, status=None, update_modified=True):
if self.is_new(): if self.is_new():
if self.get('amended_from'): if self.get('amended_from'):

View File

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

View File

@ -7,14 +7,15 @@ import unittest
import frappe, erpnext import frappe, erpnext
import frappe.model import frappe.model
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry 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 import frappe.defaults
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
test_records as pr_test_records, make_purchase_receipt, get_taxes test_records as pr_test_records, make_purchase_receipt, get_taxes
from erpnext.controllers.accounts_controller import get_payment_terms from erpnext.controllers.accounts_controller import get_payment_terms
from erpnext.exceptions import InvalidCurrency from erpnext.exceptions import InvalidCurrency
from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction 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_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"]
test_ignore = ["Serial No"] test_ignore = ["Serial No"]
@ -866,6 +867,67 @@ class TestPurchaseInvoice(unittest.TestCase):
for gle in gl_entries: for gle in gl_entries:
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) 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): def unlink_payment_on_cancel_of_invoice(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings") accounts_settings = frappe.get_doc("Accounts Settings")

View File

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

View File

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

View File

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

View File

@ -180,7 +180,7 @@ def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=No
if company: if company:
condition += "and company =%s" % (company) condition += "and company =%s" % (company)
if from_date and to_date: if from_date and to_date:
condition += "and posting_date between %s and %s" % (company, from_date, to_date) condition += "and posting_date between %s and %s" % (from_date, to_date)
## Appending the same supplier again if length of suppliers list is 1 ## Appending the same supplier again if length of suppliers list is 1
## since tuple of single element list contains None, For example ('Test Supplier 1', ) ## since tuple of single element list contains None, For example ('Test Supplier 1', )

View File

@ -602,10 +602,14 @@ def get_party_shipping_address(doctype, name):
else: else:
return '' return ''
def get_partywise_advanced_payment_amount(party_type, posting_date = None, company=None): def get_partywise_advanced_payment_amount(party_type, posting_date = None, future_payment=0, company=None):
cond = "1=1" cond = "1=1"
if posting_date: if posting_date:
cond = "posting_date <= '{0}'".format(posting_date) if future_payment:
cond = "posting_date <= '{0}' OR DATE(creation) <= '{0}' """.format(posting_date)
else:
cond = "posting_date <= '{0}'".format(posting_date)
if company: if company:
cond += "and company = '{0}'".format(company) cond += "and company = '{0}'".format(company)

View File

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

View File

@ -135,12 +135,5 @@ frappe.query_reports["Accounts Payable"] = {
} }
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('Accounts Payable', 9);
frappe.query_reports["Accounts Payable"].filters.splice(9, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});

View File

@ -104,12 +104,5 @@ frappe.query_reports["Accounts Payable Summary"] = {
} }
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('Accounts Payable Summary', 9);
frappe.query_reports["Accounts Payable Summary"].filters.splice(9, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});

View File

@ -199,12 +199,5 @@ frappe.query_reports["Accounts Receivable"] = {
} }
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('Accounts Receivable', 9);
frappe.query_reports["Accounts Receivable"].filters.splice(9, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});

View File

@ -169,9 +169,11 @@ class ReceivablePayableReport(object):
def append_subtotal_row(self, party): def append_subtotal_row(self, party):
sub_total_row = self.total_row_map.get(party) sub_total_row = self.total_row_map.get(party)
self.data.append(sub_total_row)
self.data.append({}) if sub_total_row:
self.update_sub_total_row(sub_total_row, 'Total') self.data.append(sub_total_row)
self.data.append({})
self.update_sub_total_row(sub_total_row, 'Total')
def get_voucher_balance(self, gle): def get_voucher_balance(self, gle):
if self.filters.get("sales_person"): if self.filters.get("sales_person"):
@ -232,7 +234,8 @@ class ReceivablePayableReport(object):
if self.filters.get('group_by_party'): if self.filters.get('group_by_party'):
self.append_subtotal_row(self.previous_party) self.append_subtotal_row(self.previous_party)
self.data.append(self.total_row_map.get('Total')) if self.data:
self.data.append(self.total_row_map.get('Total'))
def append_row(self, row): def append_row(self, row):
self.allocate_future_payments(row) self.allocate_future_payments(row)
@ -559,6 +562,14 @@ class ReceivablePayableReport(object):
conditions, values = self.prepare_conditions() conditions, values = self.prepare_conditions()
order_by = self.get_order_by_condition() order_by = self.get_order_by_condition()
if self.filters.show_future_payments:
values.insert(2, self.filters.report_date)
date_condition = """AND (posting_date <= %s
OR (against_voucher IS NULL AND DATE(creation) <= %s))"""
else:
date_condition = "AND posting_date <=%s"
if self.filters.get(scrub(self.party_type)): if self.filters.get(scrub(self.party_type)):
select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit" select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit"
else: else:
@ -574,9 +585,8 @@ class ReceivablePayableReport(object):
docstatus < 2 docstatus < 2
and party_type=%s and party_type=%s
and (party is not null and party != '') and (party is not null and party != '')
and posting_date <= %s {1} {2} {3}"""
{1} {2}""" .format(select_fields, date_condition, conditions, order_by), values, as_dict=True)
.format(select_fields, conditions, order_by), values, as_dict=True)
def get_sales_invoices_or_customers_based_on_sales_person(self): def get_sales_invoices_or_customers_based_on_sales_person(self):
if self.filters.get("sales_person"): if self.filters.get("sales_person"):

View File

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

View File

@ -111,7 +111,12 @@ frappe.query_reports["Accounts Receivable Summary"] = {
"fieldname":"based_on_payment_terms", "fieldname":"based_on_payment_terms",
"label": __("Based On Payment Terms"), "label": __("Based On Payment Terms"),
"fieldtype": "Check", "fieldtype": "Check",
} },
{
"fieldname":"show_future_payments",
"label": __("Show Future Payments"),
"fieldtype": "Check",
},
], ],
onload: function(report) { onload: function(report) {
@ -122,11 +127,4 @@ frappe.query_reports["Accounts Receivable Summary"] = {
} }
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('Accounts Receivable Summary', 9);
frappe.query_reports["Accounts Receivable Summary"].filters.splice(9, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});

View File

@ -33,7 +33,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
self.get_party_total(args) self.get_party_total(args)
party_advance_amount = get_partywise_advanced_payment_amount(self.party_type, party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
self.filters.report_date, self.filters.company) or {} self.filters.report_date, self.filters.show_future_payments, self.filters.company) or {}
for party, party_dict in iteritems(self.party_total): for party, party_dict in iteritems(self.party_total):
if party_dict.outstanding == 0: if party_dict.outstanding == 0:

View File

@ -93,7 +93,7 @@ def get_assets(filters):
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period, sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
from (SELECT a.asset_category, from (SELECT a.asset_category,
ifnull(sum(case when ds.schedule_date < %(from_date)s then ifnull(sum(case when ds.schedule_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
ds.depreciation_amount ds.depreciation_amount
else else
0 0
@ -111,13 +111,11 @@ def get_assets(filters):
0 0
end), 0) as depreciation_amount_during_the_period end), 0) as depreciation_amount_during_the_period
from `tabAsset` a, `tabDepreciation Schedule` ds from `tabAsset` a, `tabDepreciation Schedule` ds
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != ''
group by a.asset_category group by a.asset_category
union union
SELECT a.asset_category, SELECT a.asset_category,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then
and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s)
then
0 0
else else
a.opening_accumulated_depreciation a.opening_accumulated_depreciation

View File

@ -4,6 +4,8 @@
frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.require("assets/erpnext/js/financial_statements.js", function() {
frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements); frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements);
erpnext.utils.add_dimensions('Balance Sheet', 10);
frappe.query_reports["Balance Sheet"]["filters"].push({ frappe.query_reports["Balance Sheet"]["filters"].push({
"fieldname": "accumulated_values", "fieldname": "accumulated_values",
"label": __("Accumulated Values"), "label": __("Accumulated Values"),

View File

@ -29,37 +29,60 @@ def execute(filters=None):
for dimension in dimensions: for dimension in dimensions:
dimension_items = cam_map.get(dimension) dimension_items = cam_map.get(dimension)
if dimension_items: if dimension_items:
for account, monthwise_data in iteritems(dimension_items): data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, 0)
row = [dimension, account] else:
totals = [0, 0, 0] DCC_allocation = frappe.db.sql('''SELECT parent, sum(percentage_allocation) as percentage_allocation
for year in get_fiscal_years(filters): FROM `tabDistributed Cost Center`
last_total = 0 WHERE cost_center IN %(dimension)s
for relevant_months in period_month_ranges: AND parent NOT IN %(dimension)s
period_data = [0, 0, 0] GROUP BY parent''',{'dimension':[dimension]})
for month in relevant_months: if DCC_allocation:
if monthwise_data.get(year[0]): filters['budget_against_filter'] = [DCC_allocation[0][0]]
month_data = monthwise_data.get(year[0]).get(month, {}) cam_map = get_dimension_account_month_map(filters)
for i, fieldname in enumerate(["target", "actual", "variance"]): dimension_items = cam_map.get(DCC_allocation[0][0])
value = flt(month_data.get(fieldname)) if dimension_items:
period_data[i] += value data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation[0][1])
totals[i] += value
period_data[0] += last_total
if filters.get("show_cumulative"):
last_total = period_data[0] - period_data[1]
period_data[2] = period_data[0] - period_data[1]
row += period_data
totals[2] = totals[0] - totals[1]
if filters["period"] != "Yearly":
row += totals
data.append(row)
chart = get_chart_data(filters, columns, data) chart = get_chart_data(filters, columns, data)
return columns, data, None, chart return columns, data, None, chart
def get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation):
for account, monthwise_data in iteritems(dimension_items):
row = [dimension, account]
totals = [0, 0, 0]
for year in get_fiscal_years(filters):
last_total = 0
for relevant_months in period_month_ranges:
period_data = [0, 0, 0]
for month in relevant_months:
if monthwise_data.get(year[0]):
month_data = monthwise_data.get(year[0]).get(month, {})
for i, fieldname in enumerate(["target", "actual", "variance"]):
value = flt(month_data.get(fieldname))
period_data[i] += value
totals[i] += value
period_data[0] += last_total
if DCC_allocation:
period_data[0] = period_data[0]*(DCC_allocation/100)
period_data[1] = period_data[1]*(DCC_allocation/100)
if(filters.get("show_cumulative")):
last_total = period_data[0] - period_data[1]
period_data[2] = period_data[0] - period_data[1]
row += period_data
totals[2] = totals[0] - totals[1]
if filters["period"] != "Yearly" :
row += totals
data.append(row)
return data
def get_columns(filters): def get_columns(filters):
columns = [ columns = [
{ {
@ -366,7 +389,7 @@ def get_chart_data(filters, columns, data):
budget_values[i] += values[index] budget_values[i] += values[index]
actual_values[i] += values[index+1] actual_values[i] += values[index+1]
index += 3 index += 3
return { return {
'data': { 'data': {
'labels': labels, 'labels': labels,

View File

@ -5,6 +5,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
frappe.query_reports["Cash Flow"] = $.extend({}, frappe.query_reports["Cash Flow"] = $.extend({},
erpnext.financial_statements); erpnext.financial_statements);
erpnext.utils.add_dimensions('Cash Flow', 10);
// The last item in the array is the definition for Presentation Currency // The last item in the array is the definition for Presentation Currency
// filter. It won't be used in cash flow for now so we pop it. Please take // filter. It won't be used in cash flow for now so we pop it. Please take
// of this if you are working here. // of this if you are working here.

View File

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

View File

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

View File

@ -164,12 +164,5 @@ frappe.query_reports["General Ledger"] = {
] ]
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('General Ledger', 15)
frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});

View File

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

View File

@ -4,11 +4,18 @@
frappe.query_reports["Item-wise Purchase Register"] = { frappe.query_reports["Item-wise Purchase Register"] = {
"filters": [ "filters": [
{ {
"fieldname":"date_range", "fieldname":"from_date",
"label": __("Date Range"), "label": __("From Date"),
"fieldtype": "DateRange", "fieldtype": "Date",
"default": [frappe.datetime.add_months(frappe.datetime.get_today(),-1), frappe.datetime.get_today()], "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
"reqd": 1 "reqd": 1,
},
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.datetime.get_today(),
"reqd": 1,
}, },
{ {
"fieldname": "item_code", "fieldname": "item_code",

View File

@ -14,7 +14,6 @@ def execute(filters=None):
def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
if not filters: filters = {} if not filters: filters = {}
filters.update({"from_date": filters.get("date_range")[0], "to_date": filters.get("date_range")[1]})
columns = get_columns(additional_table_columns, filters) columns = get_columns(additional_table_columns, filters)
company_currency = erpnext.get_company_currency(filters.company) company_currency = erpnext.get_company_currency(filters.company)
@ -266,13 +265,6 @@ def get_columns(additional_table_columns, filters):
'fieldtype': 'Currency', 'fieldtype': 'Currency',
'options': 'currency', 'options': 'currency',
'width': 100 'width': 100
},
{
'fieldname': 'currency',
'label': _('Currency'),
'fieldtype': 'Currency',
'width': 80,
'hidden': 1
} }
] ]

View File

@ -4,11 +4,18 @@
frappe.query_reports["Item-wise Sales Register"] = { frappe.query_reports["Item-wise Sales Register"] = {
"filters": [ "filters": [
{ {
"fieldname": "date_range", "fieldname":"from_date",
"label": __("Date Range"), "label": __("From Date"),
"fieldtype": "DateRange", "fieldtype": "Date",
"default": [frappe.datetime.add_months(frappe.datetime.get_today(),-1), frappe.datetime.get_today()], "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
"reqd": 1 "reqd": 1,
},
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.datetime.get_today(),
"reqd": 1,
}, },
{ {
"fieldname": "customer", "fieldname": "customer",

View File

@ -14,7 +14,6 @@ def execute(filters=None):
def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
if not filters: filters = {} if not filters: filters = {}
filters.update({"from_date": filters.get("date_range") and filters.get("date_range")[0], "to_date": filters.get("date_range") and filters.get("date_range")[1]})
columns = get_columns(additional_table_columns, filters) columns = get_columns(additional_table_columns, filters)
company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency") company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency")
@ -224,7 +223,7 @@ def get_columns(additional_table_columns, filters):
} }
] ]
if filters.get('group_by') != 'Terriotory': if filters.get('group_by') != 'Territory':
columns.extend([ columns.extend([
{ {
'label': _("Territory"), 'label': _("Territory"),
@ -305,13 +304,6 @@ def get_columns(additional_table_columns, filters):
'fieldtype': 'Currency', 'fieldtype': 'Currency',
'options': 'currency', 'options': 'currency',
'width': 100 'width': 100
},
{
'fieldname': 'currency',
'label': _('Currency'),
'fieldtype': 'Currency',
'width': 80,
'hidden': 1
} }
] ]
@ -537,6 +529,13 @@ def get_tax_accounts(item_list, columns, company_currency,
'fieldtype': 'Currency', 'fieldtype': 'Currency',
'options': 'currency', 'options': 'currency',
'width': 100 'width': 100
},
{
'fieldname': 'currency',
'label': _('Currency'),
'fieldtype': 'Currency',
'width': 80,
'hidden': 1
} }
] ]

View File

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

View File

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

View File

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

View File

@ -6,6 +6,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
frappe.query_reports["Profit and Loss Statement"] = $.extend({}, frappe.query_reports["Profit and Loss Statement"] = $.extend({},
erpnext.financial_statements); erpnext.financial_statements);
erpnext.utils.add_dimensions('Profit and Loss Statement', 10);
frappe.query_reports["Profit and Loss Statement"]["filters"].push( frappe.query_reports["Profit and Loss Statement"]["filters"].push(
{ {
"fieldname": "project", "fieldname": "project",

View File

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

View File

@ -56,11 +56,4 @@ frappe.query_reports["Purchase Register"] = {
] ]
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('Purchase Register', 7);
frappe.query_reports["Purchase Register"].filters.splice(7, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});

View File

@ -68,12 +68,5 @@ frappe.query_reports["Sales Register"] = {
] ]
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('Sales Register', 7);
frappe.query_reports["Sales Register"].filters.splice(7, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});

View File

@ -102,14 +102,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"initial_depth": 3 "initial_depth": 3
} }
erpnext.dimension_filters.forEach((dimension) => { erpnext.utils.add_dimensions('Trial Balance', 6);
frappe.query_reports["Trial Balance"].filters.splice(6, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});
}); });

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -34,6 +34,11 @@
"hidden": 0, "hidden": 0,
"label": "Other Reports", "label": "Other Reports",
"links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
"label": "Regional",
"links": "[\n {\n \"description\": \"Import Italian Purchase Invoices\",\n \"label\": \"Import Supplier Invoice\",\n \"name\": \"Import Supplier Invoice\",\n \"type\": \"doctype\"\n } \n]"
} }
], ],
"cards_label": "", "cards_label": "",

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -118,7 +118,7 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEqual(po.get("items")[0].amount, 1400) self.assertEqual(po.get("items")[0].amount, 1400)
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3) self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
def test_add_new_item_in_update_child_qty_rate(self): def test_add_new_item_in_update_child_qty_rate(self):
po = create_purchase_order(do_not_save=1) po = create_purchase_order(do_not_save=1)
po.items[0].qty = 4 po.items[0].qty = 4
@ -144,7 +144,7 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEquals(len(po.get('items')), 2) self.assertEquals(len(po.get('items')), 2)
self.assertEqual(po.status, 'To Receive and Bill') self.assertEqual(po.status, 'To Receive and Bill')
def test_remove_item_in_update_child_qty_rate(self): def test_remove_item_in_update_child_qty_rate(self):
po = create_purchase_order(do_not_save=1) po = create_purchase_order(do_not_save=1)
po.items[0].qty = 4 po.items[0].qty = 4
@ -185,6 +185,23 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEquals(len(po.get('items')), 1) self.assertEquals(len(po.get('items')), 1)
self.assertEqual(po.status, 'To Receive and Bill') self.assertEqual(po.status, 'To Receive and Bill')
def test_update_child_qty_rate_perm(self):
po = create_purchase_order(item_code= "_Test Item", qty=4)
user = 'test@example.com'
test_user = frappe.get_doc('User', user)
test_user.add_roles("Accounts User")
frappe.set_user(user)
# update qty
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.items[0].name}])
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name)
# add new item
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}])
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name)
frappe.set_user("Administrator")
def test_update_qty(self): def test_update_qty(self):
po = create_purchase_order() po = create_purchase_order()
@ -689,7 +706,7 @@ class TestPurchaseOrder(unittest.TestCase):
po.save() po.save()
self.assertEqual(po.schedule_date, add_days(nowdate(), 2)) self.assertEqual(po.schedule_date, add_days(nowdate(), 2))
def test_po_optional_blanket_order(self): def test_po_optional_blanket_order(self):
""" """
Expected result: Blanket order Ordered Quantity should only be affected on Purchase Order with against_blanket_order = 1. Expected result: Blanket order Ordered Quantity should only be affected on Purchase Order with against_blanket_order = 1.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -361,7 +361,7 @@ class SellingController(StockController):
self.po_no = ', '.join(list(set([d.po_no for d in po_nos if d.po_no]))) self.po_no = ', '.join(list(set([d.po_no for d in po_nos if d.po_no])))
def set_gross_profit(self): def set_gross_profit(self):
if self.doctype == "Sales Order": if self.doctype in ["Sales Order", "Quotation"]:
for item in self.items: for item in self.items:
item.gross_profit = flt(((item.base_rate - item.valuation_rate) * item.stock_qty), self.precision("amount", item)) item.gross_profit = flt(((item.base_rate - item.valuation_rate) * item.stock_qty), self.precision("amount", item))

View File

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

View File

@ -155,7 +155,7 @@ def has_website_permission(doc, ptype, user, verbose=False):
return frappe.db.exists(doctype, get_customer_filter(doc, customers)) return frappe.db.exists(doctype, get_customer_filter(doc, customers))
elif suppliers: elif suppliers:
fieldname = 'suppliers' if doctype == 'Request for Quotation' else 'supplier' fieldname = 'suppliers' if doctype == 'Request for Quotation' else 'supplier'
return frappe.db.exists(doctype, filters={ return frappe.db.exists(doctype, {
'name': doc.name, 'name': doc.name,
fieldname: ["in", suppliers] fieldname: ["in", suppliers]
}) })

View File

@ -29,6 +29,9 @@
"requires_fulfilment", "requires_fulfilment",
"fulfilment_deadline", "fulfilment_deadline",
"fulfilment_terms", "fulfilment_terms",
"authorised_by_section",
"signee_company",
"signed_by_company",
"sb_references", "sb_references",
"document_type", "document_type",
"cb_links", "cb_links",
@ -223,10 +226,28 @@
"options": "Contract", "options": "Contract",
"print_hide": 1, "print_hide": 1,
"read_only": 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, "is_submittable": 1,
"modified": "2019-09-30 00:56:41.559681", "links": [],
"modified": "2020-03-30 06:56:07.257932",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Contract", "name": "Contract",

View File

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

View File

@ -1,306 +1,106 @@
{ {
"allow_copy": 0, "actions": [],
"allow_guest_to_view": 0, "allow_rename": 1,
"allow_import": 0, "autoname": "field:title",
"allow_rename": 0, "creation": "2018-04-16 06:44:48.791312",
"autoname": "field:title", "doctype": "DocType",
"beta": 0, "editable_grid": 1,
"creation": "2018-04-16 06:44:48.791312", "engine": "InnoDB",
"custom": 0, "field_order": [
"docstatus": 0, "title",
"doctype": "DocType", "contract_terms",
"document_type": "", "sb_fulfilment",
"editable_grid": 1, "requires_fulfilment",
"engine": "InnoDB", "fulfilment_terms"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0, "fieldname": "title",
"allow_on_submit": 0, "fieldtype": "Data",
"bold": 0, "label": "Title",
"collapsible": 0, "unique": 1
"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
},
{ {
"allow_bulk_edit": 0, "fieldname": "contract_terms",
"allow_on_submit": 0, "fieldtype": "Text Editor",
"bold": 0, "label": "Contract Terms and Conditions",
"collapsible": 0, "read_only": 1
"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
},
{ {
"allow_bulk_edit": 0, "fieldname": "sb_fulfilment",
"allow_on_submit": 0, "fieldtype": "Section Break"
"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
},
{ {
"allow_bulk_edit": 0, "default": "0",
"allow_on_submit": 0, "fieldname": "requires_fulfilment",
"bold": 0, "fieldtype": "Check",
"collapsible": 0, "label": "Requires Fulfilment"
"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
},
{ {
"allow_bulk_edit": 0, "depends_on": "eval:doc.requires_fulfilment==1",
"allow_on_submit": 0, "fieldname": "fulfilment_terms",
"bold": 0, "fieldtype": "Table",
"collapsible": 0, "label": "Fulfilment Terms and Conditions",
"columns": 0, "options": "Contract Template Fulfilment Terms"
"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
} }
], ],
"has_web_view": 0, "links": [],
"hide_heading": 0, "modified": "2020-06-03 00:24:58.179816",
"hide_toolbar": 0, "modified_by": "Administrator",
"idx": 0, "module": "CRM",
"image_view": 0, "name": "Contract Template",
"in_create": 0, "owner": "Administrator",
"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",
"permissions": [ "permissions": [
{ {
"amend": 0, "create": 1,
"apply_user_permissions": 0, "delete": 1,
"cancel": 0, "email": 1,
"create": 1, "export": 1,
"delete": 1, "print": 1,
"email": 1, "read": 1,
"export": 1, "report": 1,
"if_owner": 0, "role": "System Manager",
"import": 0, "share": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1 "write": 1
}, },
{ {
"amend": 0, "create": 1,
"apply_user_permissions": 0, "delete": 1,
"cancel": 0, "email": 1,
"create": 1, "export": 1,
"delete": 1, "print": 1,
"email": 1, "read": 1,
"export": 1, "report": 1,
"if_owner": 0, "role": "Sales Manager",
"import": 0, "share": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1 "write": 1
}, },
{ {
"amend": 0, "create": 1,
"apply_user_permissions": 0, "delete": 1,
"cancel": 0, "email": 1,
"create": 1, "export": 1,
"delete": 1, "print": 1,
"email": 1, "read": 1,
"export": 1, "report": 1,
"if_owner": 0, "role": "Purchase Manager",
"import": 0, "share": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1 "write": 1
}, },
{ {
"amend": 0, "create": 1,
"apply_user_permissions": 0, "delete": 1,
"cancel": 0, "email": 1,
"create": 1, "export": 1,
"delete": 1, "print": 1,
"email": 1, "read": 1,
"export": 1, "report": 1,
"if_owner": 0, "role": "HR Manager",
"import": 0, "share": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1 "write": 1
} }
], ],
"quick_entry": 0, "sort_field": "modified",
"read_only": 0, "sort_order": "DESC",
"read_only_onload": 0, "track_changes": 1
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,17 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'assessment_plan',
'non_standard_fieldnames': {
},
'transactions': [
{
'label': _('Assessment'),
'items': ['Assessment Result']
}
]
}

View File

@ -0,0 +1,25 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'course',
'non_standard_fieldnames': {
},
'transactions': [
{
'label': _('Course'),
'items': ['Course Enrollment', 'Course Schedule']
},
{
'label': _('Student'),
'items': ['Student Group']
},
{
'label': _('Assessment'),
'items': ['Assessment Plan']
},
]
}

View File

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

View File

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

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