Merge branch 'develop' into payment-terms

This commit is contained in:
tunde 2017-08-21 18:27:46 +01:00
commit 10fc880b06
54 changed files with 488 additions and 253 deletions

View File

@ -55,4 +55,7 @@ script:
- set -e
- bench run-tests
- sleep 5
- bench reinstall --yes
- bench execute erpnext.setup.setup_wizard.utils.complete
- bench execute erpnext.setup.utils.enable_all_roles_and_domains
- bench run-ui-tests --app erpnext

View File

@ -4,7 +4,7 @@ import inspect
import frappe
from erpnext.hooks import regional_overrides
__version__ = '8.8.3'
__version__ = '8.8.4'
def get_default_company(user=None):
'''Get default company for user'''

View File

@ -499,6 +499,7 @@ frappe.ui.form.on('Payment Entry', {
var c = frm.add_child("references");
c.reference_doctype = d.voucher_type;
c.reference_name = d.voucher_no;
c.due_date = d.due_date
c.total_amount = d.invoice_amount;
c.outstanding_amount = d.outstanding_amount;
if(!in_list(["Sales Order", "Purchase Order", "Expense Claim"], d.voucher_type)) {

View File

@ -325,23 +325,6 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}
this.frm.refresh_fields();
},
company_address: function() {
var me = this;
if(this.frm.doc.company_address) {
frappe.call({
method: "frappe.contacts.doctype.address.address.get_address_display",
args: {"address_dict": this.frm.doc.company_address },
callback: function(r) {
if(r.message) {
me.frm.set_value("company_address_display", r.message)
}
}
})
} else {
this.frm.set_value("company_address_display", "");
}
}
});

View File

@ -19,6 +19,7 @@ from erpnext.accounts.doctype.asset.depreciation \
import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal
from erpnext.stock.doctype.batch.batch import set_batch_nos
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_delivery_note_serial_no
from erpnext.setup.doctype.company.company import update_company_current_month_sales
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@ -140,7 +141,7 @@ class SalesInvoice(SellingController):
self.update_time_sheet(self.name)
frappe.enqueue('erpnext.setup.doctype.company.company.update_company_current_month_sales', company=self.company)
self.update_current_month_sales()
def validate_pos_paid_amount(self):
if len(self.payments) == 0 and self.is_pos:
@ -178,6 +179,15 @@ class SalesInvoice(SellingController):
self.make_gl_entries_on_cancel()
frappe.db.set(self, 'status', 'Cancelled')
self.update_current_month_sales()
def update_current_month_sales(self):
if frappe.flags.in_test:
update_company_current_month_sales(self.company)
else:
frappe.enqueue('erpnext.setup.doctype.company.company.update_company_current_month_sales',
company=self.company)
def update_status_updater_args(self):
if cint(self.update_stock):
self.status_updater.extend([{

View File

@ -1304,6 +1304,17 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEquals(getdate(expected_gl_entries[i][3]), getdate(gle.due_date))
def test_company_monthly_sales(self):
existing_current_month_sales = frappe.db.get_value("Company", "_Test Company", "total_monthly_sales")
si = create_sales_invoice()
current_month_sales = frappe.db.get_value("Company", "_Test Company", "total_monthly_sales")
self.assertEqual(current_month_sales, existing_current_month_sales + si.base_grand_total)
si.cancel()
current_month_sales = frappe.db.get_value("Company", "_Test Company", "total_monthly_sales")
self.assertEqual(current_month_sales, existing_current_month_sales)
def create_sales_invoice(**args):
si = frappe.new_doc("Sales Invoice")
args = frappe._dict(args)

View File

@ -8,6 +8,7 @@ from frappe import _
from frappe.model.document import Document
from frappe.utils import cstr, cint
from frappe.contacts.doctype.address.address import get_default_address
from erpnext.setup.doctype.customer_group.customer_group import get_parent_customer_groups
class IncorrectCustomerGroup(frappe.ValidationError): pass
class IncorrectSupplierType(frappe.ValidationError): pass
@ -134,6 +135,9 @@ def get_tax_template(posting_date, args):
for key, value in args.iteritems():
if key=="use_for_shopping_cart":
conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0))
if key == 'customer_group' and value:
customer_group_condition = get_customer_group_condition(value)
conditions.append("ifnull({0}, '') in ('', {1})".format(key, customer_group_condition))
else:
conditions.append("ifnull({0}, '') in ('', '{1}')".format(key, frappe.db.escape(cstr(value))))
@ -157,3 +161,8 @@ def get_tax_template(posting_date, args):
return None
return tax_template
def get_customer_group_condition(customer_group):
customer_groups = ["'%s'"%(d.name) for d in get_parent_customer_groups(frappe.db.escape(customer_group))]
condition = ",".join(['%s'] * len(customer_groups))%(tuple(customer_groups))
return condition

View File

@ -34,6 +34,14 @@ class TestTaxRule(unittest.TestCase):
tax_rule2.save()
self.assertTrue(tax_rule2.name)
def test_for_parent_customer_group(self):
tax_rule1 = make_tax_rule(customer_group= "All Customer Groups",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-01")
tax_rule1.save()
self.assertEquals(get_tax_template("2015-01-01", {"customer_group" : "Commercial"}),
"_Test Sales Taxes and Charges Template")
def test_conflict_with_overlapping_dates(self):
tax_rule1 = make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-01", to_date = "2015-01-05")

View File

@ -6,11 +6,11 @@ QUnit.test("test:POS Profile", function(assert) {
() => {
return frappe.tests.make("POS Profile", [
{naming_series: "SINV"},
{company: "_Test Company"},
{company: "Test Company"},
{country: "India"},
{currency: "INR"},
{write_off_account: "Write Off - _TC"},
{write_off_cost_center: "Main - _TC"},
{write_off_account: "Write Off - TC"},
{write_off_cost_center: "Main - TC"},
{payments: [
[
{"default": 1},
@ -35,15 +35,16 @@ QUnit.test("test:Sales Invoice", function(assert) {
frappe.run_serially([
() => {
return frappe.tests.make("Sales Invoice", [
{customer: "_Test Customer 2"},
{company: "_Test Company"},
{customer: "Test Customer 2"},
{company: "Test Company"},
{is_pos: 1},
{posting_date: frappe.datetime.get_today()},
{due_date: frappe.datetime.get_today()},
{items: [
[
{"item_code": "_Test Item"},
{"qty": 5}
{"item_code": "Test Product 1"},
{"qty": 5},
{"warehouse":'Stores - TC'}
]]
}
]);

View File

@ -79,7 +79,7 @@ def set_address_details(out, party, party_type, doctype=None, company=None):
out.shipping_address = get_address_display(out["shipping_address_name"])
out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name))
if doctype and doctype in ['Sales Invoice']:
if doctype and doctype in ['Delivery Note', 'Sales Invoice']:
out.update(get_company_address(company))
if out.company_address:
out.update(get_fetch_values(doctype, 'company_address', out.company_address))

View File

@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt
from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import get_tax_accounts
def execute(filters=None):
return _execute(filters)
@ -12,12 +13,12 @@ def execute(filters=None):
def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
if not filters: filters = {}
columns = get_columns(additional_table_columns)
last_col = len(columns)
item_list = get_items(filters, additional_query_columns)
aii_account_map = get_aii_accounts()
if item_list:
item_row_tax, tax_accounts = get_tax_accounts(item_list, columns)
itemised_tax, tax_columns = get_tax_accounts(item_list, columns,
tax_doctype="Purchase Taxes and Charges")
columns.append({
"fieldname": "currency",
@ -26,6 +27,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
"width": 80
})
company_currency = frappe.db.get_value("Company", filters.company, "default_currency")
po_pr_map = get_purchase_receipts_against_purchase_order(item_list)
data = []
for d in item_list:
@ -33,8 +35,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
if d.purchase_receipt:
purchase_receipt = d.purchase_receipt
elif d.po_detail:
purchase_receipt = ", ".join(frappe.db.sql_list("""select distinct parent
from `tabPurchase Receipt Item` where docstatus=1 and purchase_order_item=%s""", d.po_detail))
purchase_receipt = ", ".join(po_pr_map.get(d.po_detail, []))
expense_account = d.expense_account or aii_account_map.get(d.company)
row = [d.item_code, d.item_name, d.item_group, d.parent, d.posting_date, d.supplier,
@ -46,13 +47,15 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
row += [
d.credit_to, d.mode_of_payment, d.project, d.company, d.purchase_order,
purchase_receipt, expense_account, d.qty, d.base_net_rate, d.base_net_amount
purchase_receipt, expense_account, d.qty, d.stock_uom, d.base_net_rate, d.base_net_amount
]
for tax in tax_accounts:
row.append(item_row_tax.get(d.name, {}).get(tax, 0))
total_tax = 0
for tax in tax_columns:
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
row += [item_tax.get("tax_rate", 0), item_tax.get("tax_amount", 0)]
total_tax += flt(item_tax.get("tax_amount"))
total_tax = sum(row[last_col:])
row += [total_tax, d.base_net_amount + total_tax, company_currency]
data.append(row)
@ -76,7 +79,8 @@ def get_columns(additional_table_columns):
_("Mode of Payment") + ":Link/Mode of Payment:80", _("Project") + ":Link/Project:80",
_("Company") + ":Link/Company:100", _("Purchase Order") + ":Link/Purchase Order:100",
_("Purchase Receipt") + ":Link/Purchase Receipt:100", _("Expense Account") + ":Link/Account:140",
_("Qty") + ":Float:120", _("Rate") + ":Currency/currency:120", _("Amount") + ":Currency/currency:120"
_("Qty") + ":Float:120", _("Stock UOM") + "::100",
_("Rate") + ":Currency/currency:120", _("Amount") + ":Currency/currency:120"
]
return columns
@ -106,8 +110,9 @@ def get_items(filters, additional_query_columns):
pi_item.name, pi_item.parent, pi.posting_date, pi.credit_to, pi.company,
pi.supplier, pi.remarks, pi.base_net_total, pi_item.item_code, pi_item.item_name,
pi_item.item_group, pi_item.project, pi_item.purchase_order, pi_item.purchase_receipt,
pi_item.po_detail, pi_item.expense_account, pi_item.qty, pi_item.base_net_rate,
pi_item.base_net_amount, pi.supplier_name, pi.mode_of_payment {0}
pi_item.po_detail, pi_item.expense_account, pi_item.qty, pi_item.stock_uom,
pi_item.base_net_rate, pi_item.base_net_amount,
pi.supplier_name, pi.mode_of_payment {0}
from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item
where pi.name = pi_item.parent and pi.docstatus = 1 %s %s
order by pi.posting_date desc, pi_item.item_code desc
@ -116,53 +121,18 @@ def get_items(filters, additional_query_columns):
def get_aii_accounts():
return dict(frappe.db.sql("select name, stock_received_but_not_billed from tabCompany"))
def get_tax_accounts(item_list, columns):
import json
item_row_tax = {}
tax_accounts = []
invoice_item_row = {}
item_row_map = {}
for d in item_list:
invoice_item_row.setdefault(d.parent, []).append(d)
item_row_map.setdefault(d.parent, {}).setdefault(d.item_code, []).append(d)
def get_purchase_receipts_against_purchase_order(item_list):
po_pr_map = frappe._dict()
po_item_rows = list(set([d.po_detail for d in item_list]))
tax_details = frappe.db.sql("""
select
parent, account_head, item_wise_tax_detail, charge_type, base_tax_amount_after_discount_amount
from `tabPurchase Taxes and Charges`
where parenttype = 'Purchase Invoice' and docstatus = 1
and (account_head is not null and account_head != '')
and category in ('Total', 'Valuation and Total')
and parent in (%s)
""" % ', '.join(['%s']*len(invoice_item_row)), tuple(invoice_item_row.keys()))
purchase_receipts = frappe.db.sql("""
select parent, purchase_order_item
from `tabPurchase Receipt Item`
where docstatus=1 and purchase_order_item in (%s)
group by purchase_order_item, parent
""" % (', '.join(['%s']*len(po_item_rows))), tuple(po_item_rows), as_dict=1)
for parent, account_head, item_wise_tax_detail, charge_type, tax_amount in tax_details:
if account_head not in tax_accounts:
tax_accounts.append(account_head)
for pr in purchase_receipts:
po_pr_map.setdefault(pr.po_detail, []).append(pr.parent)
if item_wise_tax_detail:
try:
item_wise_tax_detail = json.loads(item_wise_tax_detail)
for item_code, tax_amount in item_wise_tax_detail.items():
tax_amount = flt(tax_amount[1]) if isinstance(tax_amount, list) else flt(tax_amount)
item_net_amount = sum([flt(d.base_net_amount)
for d in item_row_map.get(parent, {}).get(item_code, [])])
for d in item_row_map.get(parent, {}).get(item_code, []):
item_tax_amount = flt((tax_amount * d.base_net_amount) / item_net_amount) if item_net_amount else 0
item_row_tax.setdefault(d.name, {})[account_head] = item_tax_amount
except ValueError:
continue
elif charge_type == "Actual" and tax_amount:
for d in invoice_item_row.get(parent, []):
item_row_tax.setdefault(d.name, {})[account_head] = \
flt((tax_amount * d.base_net_amount) / d.base_net_total)
tax_accounts.sort()
columns += [account_head + ":Currency/currency:80" for account_head in tax_accounts]
columns += ["Total Tax:Currency/currency:80", "Total:Currency/currency:80"]
return item_row_tax, tax_accounts
return po_pr_map

View File

@ -13,11 +13,10 @@ def execute(filters=None):
def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
if not filters: filters = {}
columns = get_columns(additional_table_columns)
last_col = len(columns)
item_list = get_items(filters, additional_query_columns)
if item_list:
item_row_tax, tax_accounts = get_tax_accounts(item_list, columns)
itemised_tax, tax_columns = get_tax_accounts(item_list, columns)
columns.append({
"fieldname": "currency",
"label": _("Currency"),
@ -26,6 +25,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
})
company_currency = frappe.db.get_value("Company", filters.get("company"), "default_currency")
mode_of_payments = get_mode_of_payments(set([d.parent for d in item_list]))
so_dn_map = get_delivery_notes_against_sales_order(item_list)
data = []
for d in item_list:
@ -33,8 +33,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
if d.delivery_note:
delivery_note = d.delivery_note
elif d.so_detail:
delivery_note = ", ".join(frappe.db.sql_list("""select distinct parent
from `tabDelivery Note Item` where docstatus=1 and so_detail=%s""", d.so_detail))
delivery_note = ", ".join(so_dn_map.get(d.so_detail, []))
if not delivery_note and d.update_stock:
delivery_note = d.parent
@ -47,13 +47,16 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
row += [
d.customer_group, d.debit_to, ", ".join(mode_of_payments.get(d.parent, [])),
d.territory, d.project, d.company, d.sales_order,
delivery_note, d.income_account, d.cost_center, d.qty, d.base_net_rate, d.base_net_amount
delivery_note, d.income_account, d.cost_center, d.qty, d.stock_uom,
d.base_net_rate, d.base_net_amount
]
for tax in tax_accounts:
row.append(item_row_tax.get(d.name, {}).get(tax, 0))
total_tax = 0
for tax in tax_columns:
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
row += [item_tax.get("tax_rate", 0), item_tax.get("tax_amount", 0)]
total_tax += flt(item_tax.get("tax_amount"))
total_tax = sum(row[last_col:])
row += [total_tax, d.base_net_amount + total_tax, company_currency]
data.append(row)
@ -77,7 +80,7 @@ def get_columns(additional_table_columns):
_("Project") + ":Link/Project:80", _("Company") + ":Link/Company:100",
_("Sales Order") + ":Link/Sales Order:100", _("Delivery Note") + ":Link/Delivery Note:100",
_("Income Account") + ":Link/Account:140", _("Cost Center") + ":Link/Cost Center:140",
_("Qty") + ":Float:120",
_("Qty") + ":Float:120", _("Stock UOM") + "::100",
_("Rate") + ":Currency/currency:120",
_("Amount") + ":Currency/currency:120"
]
@ -112,62 +115,98 @@ def get_items(filters, additional_query_columns):
si_item.name, si_item.parent, si.posting_date, si.debit_to, si.project,
si.customer, si.remarks, si.territory, si.company, si.base_net_total,
si_item.item_code, si_item.item_name, si_item.item_group, si_item.sales_order,
si_item.delivery_note, si_item.income_account, si_item.cost_center, si_item.qty,
si_item.base_net_rate, si_item.base_net_amount, si.customer_name,
si.customer_group, si_item.so_detail, si.update_stock {0}
si_item.delivery_note, si_item.income_account, si_item.cost_center,
si_item.qty, si_item.stock_uom, si_item.base_net_rate, si_item.base_net_amount,
si.customer_name, si.customer_group, si_item.so_detail, si.update_stock {0}
from `tabSales Invoice` si, `tabSales Invoice Item` si_item
where si.name = si_item.parent and si.docstatus = 1 %s
order by si.posting_date desc, si_item.item_code desc
""".format(additional_query_columns or '') % conditions, filters, as_dict=1)
def get_tax_accounts(item_list, columns):
def get_delivery_notes_against_sales_order(item_list):
so_dn_map = frappe._dict()
so_item_rows = list(set([d.so_detail for d in item_list]))
delivery_notes = frappe.db.sql("""
select parent, so_detail
from `tabDelivery Note Item`
where docstatus=1 and so_detail in (%s)
group by so_detail, parent
""" % (', '.join(['%s']*len(so_item_rows))), tuple(so_item_rows), as_dict=1)
for dn in delivery_notes:
so_dn_map.setdefault(dn.so_detail, []).append(dn.parent)
return so_dn_map
def get_tax_accounts(item_list, columns, tax_doctype="Sales Taxes and Charges"):
import json
item_row_tax = {}
tax_accounts = []
invoice_item_row = {}
item_row_map = {}
tax_columns = []
invoice_item_row = {}
itemised_tax = {}
for d in item_list:
invoice_item_row.setdefault(d.parent, []).append(d)
item_row_map.setdefault(d.parent, {}).setdefault(d.item_code, []).append(d)
tax_details = frappe.db.sql("""
select
parent, account_head, item_wise_tax_detail,
parent, description, item_wise_tax_detail,
charge_type, base_tax_amount_after_discount_amount
from `tabSales Taxes and Charges`
from `tab%s`
where
parenttype = 'Sales Invoice' and docstatus = 1
and (account_head is not null and account_head != '')
and (description is not null and description != '')
and parent in (%s)
""" % ', '.join(['%s']*len(invoice_item_row)), tuple(invoice_item_row.keys()))
order by description
""" % (tax_doctype, ', '.join(['%s']*len(invoice_item_row))), tuple(invoice_item_row.keys()))
for parent, account_head, item_wise_tax_detail, charge_type, tax_amount in tax_details:
if account_head not in tax_accounts:
tax_accounts.append(account_head)
for parent, description, item_wise_tax_detail, charge_type, tax_amount in tax_details:
if description not in tax_columns and tax_amount:
tax_columns.append(description)
if item_wise_tax_detail:
try:
item_wise_tax_detail = json.loads(item_wise_tax_detail)
for item_code, tax_amount in item_wise_tax_detail.items():
tax_amount = flt(tax_amount[1]) if isinstance(tax_amount, list) else flt(tax_amount)
for item_code, tax_data in item_wise_tax_detail.items():
itemised_tax.setdefault(item_code, frappe._dict())
if isinstance(tax_data, list):
tax_rate, tax_amount = tax_data
else:
tax_rate = tax_data
tax_amount = 0
if charge_type == "Actual" and not tax_rate:
tax_rate = "NA"
item_net_amount = sum([flt(d.base_net_amount)
for d in item_row_map.get(parent, {}).get(item_code, [])])
for d in item_row_map.get(parent, {}).get(item_code, []):
item_tax_amount = flt((tax_amount * d.base_net_amount) / item_net_amount) if item_net_amount else 0
item_row_tax.setdefault(d.name, {})[account_head] = item_tax_amount
item_tax_amount = flt((tax_amount * d.base_net_amount) / item_net_amount) \
if item_net_amount else 0
if item_tax_amount:
itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
"tax_rate": tax_rate,
"tax_amount": item_tax_amount
})
except ValueError:
continue
elif charge_type == "Actual" and tax_amount:
for d in invoice_item_row.get(parent, []):
item_row_tax.setdefault(d.name, {})[account_head] = \
flt((tax_amount * d.base_net_amount) / d.base_net_total)
itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
"tax_rate": "NA",
"tax_amount": flt((tax_amount * d.base_net_amount) / d.base_net_total)
})
tax_accounts.sort()
columns += [account_head + ":Currency/currency:80" for account_head in tax_accounts]
columns += ["Total Tax:Currency/currency:80", "Total:Currency/currency:80"]
tax_columns.sort()
for desc in tax_columns:
columns.append(desc + " Rate:Data:80")
columns.append(desc + " Amount:Currency/currency:100")
return item_row_tax, tax_accounts
columns += ["Total Tax:Currency/currency:80", "Total:Currency/currency:100"]
return itemised_tax, tax_columns

View File

@ -580,9 +580,15 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
payment_dr_or_cr = "payment_gl_entry.debit_in_account_currency - payment_gl_entry.credit_in_account_currency"
invoice = 'Sales Invoice' if party_type == 'Customer' else 'Purchase Invoice'
invoice_list = frappe.db.sql("""
select
voucher_no, voucher_type, posting_date, ifnull(sum({dr_or_cr}), 0) as invoice_amount, due_date,
(
case when (voucher_type = 'Sales Invoice' or voucher_type = 'Purchase Invoice')
then (select due_date from `tab{invoice}` where name = voucher_no)
else posting_date end
) as due_date,
(
select ifnull(sum({payment_dr_or_cr}), 0)
from `tabGL Entry` payment_gl_entry
@ -607,6 +613,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
having (invoice_amount - payment_amount) > 0.005
order by posting_date, name, due_date""".format(
dr_or_cr = dr_or_cr,
invoice = invoice,
payment_dr_or_cr = payment_dr_or_cr,
condition = condition or ""
), {
@ -621,6 +628,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
outstanding_invoices.append(frappe._dict({
'voucher_no': d.voucher_no,
'voucher_type': d.voucher_type,
'due_date': d.due_date,
'posting_date': d.posting_date,
'invoice_amount': flt(d.invoice_amount),
'payment_amount': flt(d.payment_amount),

View File

@ -8,10 +8,8 @@ QUnit.test("test: purchase order", function(assert) {
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{company: 'Wind Power LLC'},
{is_subcontracted: 'No'},
{buying_price_list: 'Test-Buying-USD'},
{currency: 'USD'},
{currency: 'INR'},
{items: [
[
{"item_code": 'Test Product 4'},
@ -20,7 +18,7 @@ QUnit.test("test: purchase order", function(assert) {
{"qty": 5},
{"uom": 'Unit'},
{"rate": 100},
{"warehouse": 'Stores - WP'}
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]},

View File

@ -8,7 +8,6 @@ QUnit.test("test: purchase order with get items", function(assert) {
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{company: 'Wind Power LLC'},
{is_subcontracted: 'No'},
{buying_price_list: 'Test-Buying-USD'},
{currency: 'USD'},
@ -18,7 +17,7 @@ QUnit.test("test: purchase order with get items", function(assert) {
{"qty": 5},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"warehouse": 'Stores - WP'}
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]}
]);
@ -46,9 +45,9 @@ QUnit.test("test: purchase order with get items", function(assert) {
assert.ok(cur_frm.doc.items[3].item_name == 'Keyboard', "Product bundle item 3 correct");
},
() => cur_frm.doc.items[1].warehouse = 'Stores - WP',
() => cur_frm.doc.items[2].warehouse = 'Stores - WP',
() => cur_frm.doc.items[3].warehouse = 'Stores - WP',
() => cur_frm.doc.items[1].warehouse = 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company")),
() => cur_frm.doc.items[2].warehouse = 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company")),
() => cur_frm.doc.items[3].warehouse = 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company")),
() => cur_frm.save(),
() => frappe.timeout(1),

View File

@ -8,7 +8,6 @@ QUnit.test("test: purchase order receipt", function(assert) {
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{company: 'Wind Power LLC'},
{is_subcontracted: 'No'},
{buying_price_list: 'Test-Buying-USD'},
{currency: 'USD'},
@ -20,7 +19,7 @@ QUnit.test("test: purchase order receipt", function(assert) {
{"qty": 5},
{"uom": 'Unit'},
{"rate": 100},
{"warehouse": 'Stores - WP'}
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]},
]);
@ -69,9 +68,9 @@ QUnit.test("test: purchase order receipt", function(assert) {
() => {
assert.ok($('div.slick-cell.l2.r2 > a').text().includes('Test Product 1')
&& $('div.slick-cell.l9.r9 > div').text().includes(5)
&& $('div.slick-cell.l12.r12 > div').text().includes(100), "Stock ledger entry correct");
&& $('div.slick-cell.l12.r12 > div').text().includes(433.29), "Stock ledger entry correct",$('div.slick-cell.l12.r12 > div').text());
},
() => done()
]);
});
});

View File

@ -8,7 +8,6 @@ QUnit.test("test: purchase order with discount on grand total", function(assert)
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{company: 'Wind Power LLC'},
{is_subcontracted: 'No'},
{buying_price_list: 'Test-Buying-EUR'},
{currency: 'EUR'},
@ -20,7 +19,7 @@ QUnit.test("test: purchase order with discount on grand total", function(assert)
{"rate": 500 },
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"warehouse": 'Stores - WP'}
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]},
{apply_discount_on: 'Grand Total'},

View File

@ -8,7 +8,6 @@ QUnit.test("test: purchase order with item wise discount", function(assert) {
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{company: 'Wind Power LLC'},
{is_subcontracted: 'No'},
{buying_price_list: 'Test-Buying-EUR'},
{currency: 'EUR'},
@ -19,7 +18,7 @@ QUnit.test("test: purchase order with item wise discount", function(assert) {
{"uom": 'Unit'},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"warehouse": 'Stores - WP'},
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))},
{"discount_percentage": 20}
]
]}

View File

@ -8,7 +8,6 @@ QUnit.test("test: purchase order with multi UOM", function(assert) {
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{company: 'Wind Power LLC'},
{is_subcontracted: 'No'},
{items: [
[
@ -18,7 +17,7 @@ QUnit.test("test: purchase order with multi UOM", function(assert) {
{"rate": 100},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"warehouse": 'Stores - WP'}
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]}
]);

View File

@ -8,7 +8,6 @@ QUnit.test("test: purchase order with taxes and charges", function(assert) {
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{company: 'Wind Power LLC'},
{is_subcontracted: 'No'},
{buying_price_list: 'Test-Buying-USD'},
{currency: 'USD'},
@ -20,7 +19,7 @@ QUnit.test("test: purchase order with taxes and charges", function(assert) {
{"rate": 500 },
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"warehouse": 'Stores - WP'}
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]},

View File

@ -196,6 +196,7 @@ def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context
list_context = get_list_context(context)
list_context["show_sidebar"] = True
list_context["title"] = "Request for Quotation"
return list_context
def get_supplier_contacts(doctype, txt, searchfield, start, page_len, filters):

View File

@ -9,7 +9,6 @@ QUnit.test("test: request_for_quotation", function(assert) {
date = frappe.datetime.add_days(frappe.datetime.now_date(), 10);
return frappe.tests.make('Request for Quotation', [
{transaction_date: date},
{company: 'Wind Power LLC'},
{suppliers: [
[
{"supplier": 'Test Supplier'},
@ -21,7 +20,7 @@ QUnit.test("test: request_for_quotation", function(assert) {
{"item_code": 'Test Product 4'},
{"qty": 5},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(),20)},
{"warehouse": 'All Warehouses - WP'}
{"warehouse": 'All Warehouses - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]},
{message_for_supplier: 'Please supply the specified items at the best possible rates'},
@ -30,12 +29,12 @@ QUnit.test("test: request_for_quotation", function(assert) {
},
() => {
assert.ok(cur_frm.doc.transaction_date == date, "Date correct");
assert.ok(cur_frm.doc.company == 'Wind Power LLC', "Company correct");
assert.ok(cur_frm.doc.company == cur_frm.doc.company, "Company correct");
assert.ok(cur_frm.doc.suppliers[0].supplier_name == 'Test Supplier', "Supplier name correct");
assert.ok(cur_frm.doc.suppliers[0].contact == 'Contact 3-Test Supplier', "Contact correct");
assert.ok(cur_frm.doc.suppliers[0].email_id == 'test@supplier.com', "Email id correct");
assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item Name correct");
assert.ok(cur_frm.doc.items[0].warehouse == 'All Warehouses - WP', "Warehouse correct");
assert.ok(cur_frm.doc.items[0].warehouse == 'All Warehouses - '+frappe.get_abbr(frappe.defaults.get_default("Company")), "Warehouse correct");
assert.ok(cur_frm.doc.message_for_supplier == 'Please supply the specified items at the best possible rates', "Reply correct");
assert.ok(cur_frm.doc.tc_name == 'Test Term 1', "Term name correct");
},

View File

@ -8,9 +8,8 @@ QUnit.test("test: supplier", function(assert) {
return frappe.tests.make('Supplier', [
{supplier_name: 'Test Supplier'},
{supplier_type: 'Hardware'},
{country: 'United States'},
{default_currency: 'USD'},
{default_price_list: 'Test-Buying-USD'},
{country: 'India'},
{default_currency: 'INR'},
{credit_days_based_on: 'Fixed Days'},
{accounts: [
[
@ -68,7 +67,7 @@ QUnit.test("test: supplier", function(assert) {
() => {
assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Name correct");
assert.ok(cur_frm.doc.supplier_type == 'Hardware', "Type correct");
assert.ok(cur_frm.doc.default_currency == 'USD', "Currency correct");
assert.ok(cur_frm.doc.default_currency == 'INR', "Currency correct");
assert.ok(cur_frm.doc.accounts[0].account == 'Creditors - '+frappe.get_abbr('Test Company'), " Account Head abbr correct");
assert.ok($('.address-box:nth-child(3) p').text().includes('Shipping City 3'), "Address correct");
assert.ok($('.col-sm-6+ .col-sm-6 .h6').text().includes('Contact 3'), "Contact correct");

View File

@ -11,16 +11,14 @@ QUnit.test("test: supplier quotation", function(assert) {
return frappe.tests.make('Supplier Quotation', [
{supplier: 'Test Supplier'},
{transaction_date: date},
{company: 'Wind Power LLC'},
{buying_price_list: 'Test-Buying-USD'},
{currency: 'USD'},
{currency: 'INR'},
{items: [
[
{"item_code": 'Test Product 4'},
{"qty": 5},
{"uom": 'Unit'},
{"rate": 200},
{"warehouse": 'All Warehouses - WP'}
{"warehouse": 'All Warehouses - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]},
{apply_discount_on: 'Grand Total'},
@ -32,7 +30,7 @@ QUnit.test("test: supplier quotation", function(assert) {
() => {
// Get Supplier details
assert.ok(cur_frm.doc.supplier == 'Test Supplier', "Supplier correct");
assert.ok(cur_frm.doc.company == 'Wind Power LLC', "Company correct");
assert.ok(cur_frm.doc.company == cur_frm.doc.company, "Company correct");
// Get Contact details
assert.ok(cur_frm.doc.contact_display == 'Contact 3', "Conatct correct");
assert.ok(cur_frm.doc.contact_email == 'test@supplier.com', "Email correct");

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

View File

@ -1,5 +1,6 @@
print-settings
print-format-builder
print-style
print-headings
letter-head
address-template

View File

@ -0,0 +1,15 @@
# Print Style
Frappe/ERPNext comes with pre-set styles for your printed documents. You can also create new styles using CSS that can be applied to all your print formats.
To create a new **Print Style** go to **Setup > Printing and Branding > Print Style**, or just type "print style" in the search bar.
Here you can define the CSS rules for your print formats. These apply to both standard and custom print formats. To find out the various classes available, you can make a standard print format, open in a new page and see the source.
To set a default style, you can go to [Print Settings](/docs/setup/print/print-settings)
All Print Format styles are based on Bootstrap (Version 3) CSS Framework.
<img class="screenshot" alt="Print Style" src="/docs/assets/img/setup/print/print-style.png">
{next}

View File

@ -231,12 +231,12 @@ class SalarySlip(TransactionBase):
holidays = self.get_holidays_for_employee(self.start_date, self.end_date)
working_days = date_diff(self.end_date, self.start_date) + 1
actual_lwp = self.calculate_lwp(holidays, working_days)
if not cint(frappe.db.get_value("HR Settings", None, "include_holidays_in_total_working_days")):
working_days -= len(holidays)
if working_days < 0:
frappe.throw(_("There are more holidays than working days this month."))
actual_lwp = self.calculate_lwp(holidays, working_days)
if not lwp:
lwp = actual_lwp
elif lwp != actual_lwp:

View File

@ -46,7 +46,7 @@ QUnit.test("test Salary Structure", function(assert) {
{ payment_account: 'CASH - TC'},
]);
},
() => frappe.timeout(9),
() => frappe.timeout(10),
() => cur_dialog.set_value('value','Test Salary Structure'),
() => frappe.timeout(2),
() => frappe.click_button('Create'),
@ -75,7 +75,7 @@ QUnit.test("test Salary Structure", function(assert) {
};
frappe.run_serially([
() => salary_structure('Test Employee 1','Test Employee 3'),
() => frappe.timeout(14),
() => frappe.timeout(16),
() => done()
]);
});

View File

@ -112,11 +112,11 @@ QUnit.test("test: production order", function (assert) {
() => click_make(),
() => {
assert.equal(cur_frm.doc.total_incoming_value, "105700",
"Incoming cost is correct"); // Price of each item x5, values are in USD
"Incoming cost is correct "+cur_frm.doc.total_incoming_value); // Price of each item x5, values are in INR
assert.equal(cur_frm.doc.total_outgoing_value, "99000",
"Outgoing cost is correct"); // Price of each item x5, values are in USD
"Outgoing cost is correct"); // Price of each item x5, values are in INR
assert.equal(cur_frm.doc.total_incoming_value - cur_frm.doc.total_outgoing_value, cur_frm.doc.value_difference,
"Value difference is correct"); // Price of each item x5, values are in USD
"Value difference is correct"); // Price of each item x5, values are in INR
},
() => frappe.click_button("Save"),
() => frappe.timeout(1),
@ -135,4 +135,4 @@ QUnit.test("test: production order", function (assert) {
() => done()
]);
});
});

View File

@ -421,7 +421,7 @@ erpnext.patches.v8_1.removed_report_support_hours
erpnext.patches.v8_1.add_indexes_in_transaction_doctypes
erpnext.patches.v8_3.set_restrict_to_domain_for_module_def
erpnext.patches.v8_1.update_expense_claim_status
erpnext.patches.v8_3.update_company_total_sales
erpnext.patches.v8_3.update_company_total_sales #2017-08-16
erpnext.patches.v8_4.make_scorecard_records
erpnext.patches.v8_1.set_delivery_date_in_so_item #2017-07-28
erpnext.patches.v8_5.fix_tax_breakup_for_non_invoice_docs
@ -432,3 +432,4 @@ erpnext.patches.v8_5.update_customer_group_in_POS_profile
erpnext.patches.v8_6.update_timesheet_company_from_PO
erpnext.patches.v8_6.set_write_permission_for_quotation_for_sales_manager
erpnext.patches.v8_5.remove_project_type_property_setter
erpnext.patches.v8_7.add_more_gst_fields

View File

View File

@ -0,0 +1,9 @@
import frappe
from erpnext.regional.india.setup import make_custom_fields
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
return
make_custom_fields()

View File

@ -346,7 +346,21 @@ erpnext.buying.get_items_from_product_bundle = function(frm) {
},
freeze: true,
callback: function(r) {
const first_row_is_empty = function(child_table){
if($.isArray(child_table) && child_table.length > 0) {
return !child_table[0].item_code;
}
return false;
};
const remove_empty_first_row = function(frm){
if (first_row_is_empty(frm.doc.items)){
frm.doc.items = frm.doc.items.splice(1);
}
};
if(!r.exc && r.message) {
remove_empty_first_row(frm);
for ( var i=0; i< r.message.length; i++ ) {
var d = frm.add_child("items");
var item = r.message[i];
@ -366,3 +380,4 @@ erpnext.buying.get_items_from_product_bundle = function(frm) {
});
dialog.show();
}

View File

@ -62,7 +62,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
this.frm.doc.conversion_rate = flt(this.frm.doc.conversion_rate, (cur_frm) ? precision("conversion_rate") : 9);
var conversion_rate_label = frappe.meta.get_label(this.frm.doc.doctype, "conversion_rate",
this.frm.doc.name);
var company_currency = this.frm.doc.currency || this.get_company_currency();
var company_currency = this.get_company_currency();
if(!this.frm.doc.conversion_rate) {
if(this.frm.doc.currency == company_currency) {
@ -419,7 +419,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
this.frm.doc.currency, precision("rounded_total"));
}
if(frappe.meta.get_docfield(this.frm.doc.doctype, "base_rounded_total", this.frm.doc.name)) {
var company_currency = this.frm.doc.currency || this.get_company_currency();
var company_currency = this.get_company_currency();
this.frm.doc.base_rounded_total =
round_based_on_smallest_currency_fraction(this.frm.doc.base_grand_total,

View File

@ -147,7 +147,7 @@
{
"state_number": "22",
"state_code": "CT",
"state_name": "Chattisgarh"
"state_name": "Chhattisgarh"
},
{
"state_number": "04",

View File

@ -81,6 +81,47 @@ def add_print_formats():
def make_custom_fields():
hsn_sac_field = dict(fieldname='gst_hsn_code', label='HSN/SAC',
fieldtype='Data', options='item_code.gst_hsn_code', insert_after='description', print_hide=1)
invoice_gst_fields = [
dict(fieldname='gst_section', label='GST Details', fieldtype='Section Break',
insert_after='select_print_heading', print_hide=1, collapsible=1),
dict(fieldname='invoice_copy', label='Invoice Copy',
fieldtype='Select', insert_after='gst_section', print_hide=1, allow_on_submit=1,
options='Original for Recipient\nDuplicate for Transporter\nDuplicate for Supplier\nTriplicate for Supplier'),
dict(fieldname='reverse_charge', label='Reverse Charge',
fieldtype='Select', insert_after='invoice_copy', print_hide=1,
options='Y\nN', default='N'),
dict(fieldname='gst_col_break', fieldtype='Column Break', insert_after='reverse_charge'),
dict(fieldname='invoice_type', label='Invoice Type',
fieldtype='Select', insert_after='reverse_charge', print_hide=1,
options='Regular\nSEZ\nExport\nDeemed Export', default='Regular'),
dict(fieldname='export_type', label='Export Type',
fieldtype='Select', insert_after='invoice_type', print_hide=1,
depends_on='eval:in_list(["SEZ", "Export", "Deemed Export"], doc.invoice_type)',
options='\nWith Payment of Tax\nWithout Payment of Tax'),
dict(fieldname='ecommerce_gstin', label='E-commerce GSTIN',
fieldtype='Data', insert_after='export_type', print_hide=1)
]
purchase_invoice_gst_fields = [
dict(fieldname='supplier_gstin', label='Supplier GSTIN',
fieldtype='Data', insert_after='supplier_address',
options='supplier_address.gstin', print_hide=1),
dict(fieldname='company_gstin', label='Company GSTIN',
fieldtype='Data', insert_after='shipping_address',
options='shipping_address.gstin', print_hide=1)
]
sales_invoice_gst_fields = [
dict(fieldname='customer_gstin', label='Customer GSTIN',
fieldtype='Data', insert_after='shipping_address',
options='shipping_address_name.gstin', print_hide=1),
dict(fieldname='place_of_supply', label='Place of Supply',
fieldtype='Data', insert_after='customer_gstin', print_hide=1,
options='shipping_address_name.gst_state_number', read_only=1),
dict(fieldname='company_gstin', label='Company GSTIN',
fieldtype='Data', insert_after='company_address',
options='company_address.gstin', print_hide=1)
]
custom_fields = {
'Address': [
@ -91,25 +132,9 @@ def make_custom_fields():
dict(fieldname='gst_state_number', label='GST State Number',
fieldtype='Int', insert_after='gst_state', read_only=1),
],
'Purchase Invoice': [
dict(fieldname='supplier_gstin', label='Supplier GSTIN',
fieldtype='Data', insert_after='supplier_address',
options='supplier_address.gstin', print_hide=1),
dict(fieldname='company_gstin', label='Company GSTIN',
fieldtype='Data', insert_after='shipping_address',
options='shipping_address.gstin', print_hide=1),
],
'Sales Invoice': [
dict(fieldname='customer_gstin', label='Customer GSTIN',
fieldtype='Data', insert_after='shipping_address',
options='shipping_address_name.gstin', print_hide=1),
dict(fieldname='company_gstin', label='Company GSTIN',
fieldtype='Data', insert_after='company_address',
options='company_address.gstin', print_hide=1),
dict(fieldname='invoice_copy', label='Invoice Copy',
fieldtype='Select', insert_after='select_print_heading', print_hide=1, allow_on_submit=1,
options='Original for Recipient\nDuplicate for Transporter\nDuplicate for Supplier\nTriplicate for Supplier')
],
'Purchase Invoice': purchase_invoice_gst_fields + invoice_gst_fields,
'Sales Invoice': sales_invoice_gst_fields + invoice_gst_fields,
"Delivery Note": sales_invoice_gst_fields,
'Item': [
dict(fieldname='gst_hsn_code', label='HSN/SAC',
fieldtype='Link', options='GST HSN Code', insert_after='item_group'),

View File

@ -9,9 +9,17 @@ def execute(filters=None):
return _execute(filters, additional_table_columns=[
dict(fieldtype='Data', label='Supplier GSTIN', width=120),
dict(fieldtype='Data', label='Company GSTIN', width=120),
dict(fieldtype='Data', label='Reverse Charge', width=120),
dict(fieldtype='Data', label='Invoice Type', width=120),
dict(fieldtype='Data', label='Export Type', width=120),
dict(fieldtype='Data', label='E-Commerce GSTIN', width=130),
dict(fieldtype='Data', label='HSN Code', width=120)
], additional_query_columns=[
'supplier_gstin',
'company_gstin',
'reverse_charge',
'invoice_type',
'export_type',
'ecommerce_gstin',
'gst_hsn_code'
])

View File

@ -9,9 +9,19 @@ def execute(filters=None):
return _execute(filters, additional_table_columns=[
dict(fieldtype='Data', label='Customer GSTIN', width=120),
dict(fieldtype='Data', label='Company GSTIN', width=120),
dict(fieldtype='Data', label='Place of Supply', width=120),
dict(fieldtype='Data', label='Reverse Charge', width=120),
dict(fieldtype='Data', label='Invoice Type', width=120),
dict(fieldtype='Data', label='Export Type', width=120),
dict(fieldtype='Data', label='E-Commerce GSTIN', width=130),
dict(fieldtype='Data', label='HSN Code', width=120)
], additional_query_columns=[
'customer_gstin',
'company_gstin',
'place_of_supply',
'reverse_charge',
'invoice_type',
'export_type',
'ecommerce_gstin',
'gst_hsn_code'
])

View File

@ -8,9 +8,17 @@ from erpnext.accounts.report.purchase_register.purchase_register import _execute
def execute(filters=None):
return _execute(filters, additional_table_columns=[
dict(fieldtype='Data', label='Supplier GSTIN', width=120),
dict(fieldtype='Data', label='Company GSTIN', width=120)
dict(fieldtype='Data', label='Company GSTIN', width=120),
dict(fieldtype='Data', label='Reverse Charge', width=120),
dict(fieldtype='Data', label='Invoice Type', width=120),
dict(fieldtype='Data', label='Export Type', width=120),
dict(fieldtype='Data', label='E-Commerce GSTIN', width=130)
], additional_query_columns=[
'supplier_gstin',
'company_gstin'
'company_gstin',
'reverse_charge',
'invoice_type',
'export_type',
'ecommerce_gstin'
])

View File

@ -8,8 +8,18 @@ from erpnext.accounts.report.sales_register.sales_register import _execute
def execute(filters=None):
return _execute(filters, additional_table_columns=[
dict(fieldtype='Data', label='Customer GSTIN', width=120),
dict(fieldtype='Data', label='Company GSTIN', width=120)
dict(fieldtype='Data', label='Company GSTIN', width=120),
dict(fieldtype='Data', label='Place of Supply', width=120),
dict(fieldtype='Data', label='Reverse Charge', width=120),
dict(fieldtype='Data', label='Invoice Type', width=120),
dict(fieldtype='Data', label='Export Type', width=120),
dict(fieldtype='Data', label='E-Commerce GSTIN', width=130)
], additional_query_columns=[
'customer_gstin',
'company_gstin'
'company_gstin',
'place_of_supply',
'reverse_charge',
'invoice_type',
'export_type',
'ecommerce_gstin'
])

View File

@ -15,6 +15,7 @@ QUnit.test('test student applicant', function(assert){
() => frappe.timeout(0.5),
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(0.5),
() => {
testing_status = $('span.indicator.orange').text();
assert.ok(testing_status.indexOf('Submit this document to confirm') == -1); // checking if submit has been successfull

View File

@ -82,7 +82,7 @@ def get_guardian_map(student_list):
select parent, guardian, guardian_name, relation from `tabStudent Guardian` where parent in (%s)''' %
', '.join(['%s']*len(student_list)), tuple(student_list), as_dict=1)
guardian_list = list(set([g.guardian for g in guardian_details]))
guardian_list = list(set([g.guardian for g in guardian_details])) or ['']
guardian_mobile_no = dict(frappe.db.sql("""select name, mobile_number from `tabGuardian`
where name in (%s)""" % ", ".join(['%s']*len(guardian_list)), tuple(guardian_list)))

View File

@ -179,7 +179,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
doc: this.frm.doc,
method: 'get_production_order_items',
callback: function(r) {
if(!r.message.every(function(d) { return !!d.bom })) {
if(!r.message) {
frappe.msgprint({
title: __('Production Order not created'),
message: __('No Items with Bill of Materials to Manufacture'),

View File

@ -330,18 +330,19 @@ class SalesOrder(SellingController):
def get_production_order_items(self):
'''Returns items with BOM that already do not have a linked production order'''
items = []
for i in self.packed_items or self.items:
bom = frappe.get_all('BOM', dict(item=i.item_code, is_active=True),
order_by='is_default desc')
bom = bom[0].name if bom else None
stock_qty = i.qty if self.packed_items else i.stock_qty
items.append(dict(
item_code= i.item_code,
bom = bom,
warehouse = i.warehouse,
pending_qty= stock_qty - flt(frappe.db.sql('''select sum(qty) from `tabProduction Order`
where production_item=%s and sales_order=%s''', (i.item_code, self.name))[0][0])
))
for table in [self.items, self.packed_items]:
for i in table:
bom = get_default_bom_item(i.item_code)
if bom:
stock_qty = i.qty if i.doctype == 'Packed Item' else i.stock_qty
items.append(dict(
item_code= i.item_code,
bom = bom,
warehouse = i.warehouse,
pending_qty= stock_qty - flt(frappe.db.sql('''select sum(qty) from `tabProduction Order`
where production_item=%s and sales_order=%s''', (i.item_code, self.name))[0][0])
))
return items
@ -465,6 +466,11 @@ def make_delivery_note(source_name, target_doc=None):
target.ignore_pricing_rule = 1
target.run_method("set_missing_values")
target.run_method("calculate_taxes_and_totals")
# set company address
target.update(get_company_address(target.company))
if target.company_address:
target.update(get_fetch_values("Delivery Note", 'company_address', target.company_address))
def update_item(source, target, source_parent):
target.base_amount = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.base_rate)
@ -774,3 +780,10 @@ def make_production_orders(items, sales_order, company, project=None):
def update_status(status, name):
so = frappe.get_doc("Sales Order", name)
so.update_status(status)
def get_default_bom_item(item_code):
bom = frappe.get_all('BOM', dict(item=item_code, is_active=True),
order_by='is_default desc')
bom = bom[0].name if bom else None
return bom

View File

@ -326,6 +326,23 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
this.calculate_taxes_and_totals();
cur_frm.refresh_fields();
}
},
company_address: function() {
var me = this;
if(this.frm.doc.company_address) {
frappe.call({
method: "frappe.contacts.doctype.address.address.get_address_display",
args: {"address_dict": this.frm.doc.company_address },
callback: function(r) {
if(r.message) {
me.frm.set_value("company_address_display", r.message)
}
}
})
} else {
this.frm.set_value("company_address_display", "");
}
}
});

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe, os
from frappe import _
from frappe.utils import cint
from frappe.utils import cint, today, formatdate
import frappe.defaults
@ -312,39 +312,35 @@ def get_name_with_abbr(name, company):
return " - ".join(parts)
def update_company_current_month_sales(company):
from frappe.utils import today, formatdate
current_month_year = formatdate(today(), "MM-yyyy")
results = frappe.db.sql(('''
results = frappe.db.sql('''
select
sum(base_grand_total) as total, date_format(posting_date, '%m-%Y') as month_year
from
`tabSales Invoice`
where
date_format(posting_date, '%m-%Y')="{0}" and
company = "{1}"
date_format(posting_date, '%m-%Y')="{0}"
and docstatus = 1
and company = "{1}"
group by
month_year;
''').format(current_month_year, frappe.db.escape(company)), as_dict = True)
month_year
'''.format(current_month_year, frappe.db.escape(company)), as_dict = True)
monthly_total = results[0]['total'] if len(results) > 0 else 0
frappe.db.sql(('''
update tabCompany set total_monthly_sales = %s where name=%s
'''), (monthly_total, frappe.db.escape(company)))
frappe.db.set_value("Company", company, "total_monthly_sales", monthly_total)
frappe.db.commit()
def update_company_monthly_sales(company):
'''Cache past year monthly sales of every company based on sales invoices'''
from frappe.utils.goal import get_monthly_results
import json
filter_str = "company = '{0}' and status != 'Draft'".format(frappe.db.escape(company))
month_to_value_dict = get_monthly_results("Sales Invoice", "base_grand_total", "posting_date", filter_str, "sum")
filter_str = "company = '{0}' and status != 'Draft' and docstatus=1".format(frappe.db.escape(company))
month_to_value_dict = get_monthly_results("Sales Invoice", "base_grand_total",
"posting_date", filter_str, "sum")
frappe.db.sql(('''
update tabCompany set sales_monthly_history = %s where name=%s
'''), (json.dumps(month_to_value_dict), frappe.db.escape(company)))
frappe.db.set_value("Company", company, "sales_monthly_history", json.dumps(month_to_value_dict))
frappe.db.commit()
def cache_companies_monthly_sales_history():

View File

@ -27,6 +27,10 @@ def delete_company_transactions(company_name):
"Purchase Taxes and Charges Template", "POS Profile", 'BOM'):
delete_for_doctype(doctype, company_name)
# reset company values
doc.total_monthly_sales = 0
doc.sales_monthly_history = None
doc.save()
# Clear notification counts
clear_notifications()

View File

@ -10,7 +10,7 @@ QUnit.test("Test: Company", function (assert) {
() => frappe.timeout(1),
() => cur_frm.set_value("company_name", "Razer Blade"),
() => cur_frm.set_value("abbr", "RB"),
() => cur_frm.set_value("default_currency", "USD"),
() => cur_frm.set_value("default_currency", "INR"),
() => cur_frm.save(),
() => frappe.timeout(1),

View File

@ -18,3 +18,10 @@ class CustomerGroup(NestedSet):
def validate_name_with_customer(self):
if frappe.db.exists("Customer", self.name):
frappe.msgprint(_("An Customer exists with same name"), raise_exception=1)
def get_parent_customer_groups(customer_group):
lft, rgt = frappe.db.get_value("Customer Group", customer_group, ['lft', 'rgt'])
return frappe.db.sql("""select name from `tabCustomer Group`
where lft <= %s and rgt >= %s
order by lft asc""", (lft, rgt), as_dict=True)

View File

@ -1,7 +1,7 @@
{
"add_sample_data": 1,
"bank_account": "HDFC",
"company_abbr": "GT",
"company_abbr": "FT",
"company_name": "For Testing",
"company_tagline": "Just for GST",
"country": "India",

View File

@ -860,22 +860,22 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "",
"fieldname": "customer_group",
"fieldname": "company_address",
"fieldtype": "Link",
"hidden": 1,
"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": "Customer Group",
"label": "Company Address Name",
"length": 0,
"no_copy": 0,
"options": "Customer Group",
"options": "Address",
"permlevel": 0,
"print_hide": 1,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
@ -891,9 +891,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "",
"fieldname": "territory",
"fieldtype": "Link",
"fieldname": "company_address_display",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -901,12 +900,12 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Territory",
"label": "Company Address",
"length": 0,
"no_copy": 0,
"options": "Territory",
"permlevel": 0,
"print_hide": 1,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
@ -2757,6 +2756,68 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "",
"fieldname": "customer_group",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Customer Group",
"length": 0,
"no_copy": 0,
"options": "Customer Group",
"permlevel": 0,
"print_hide": 1,
"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,
"description": "",
"fieldname": "territory",
"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": "Territory",
"length": 0,
"no_copy": 0,
"options": "Territory",
"permlevel": 0,
"print_hide": 1,
"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,
@ -3426,7 +3487,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-07-19 13:48:09.630820",
"modified": "2017-08-09 15:44:14.253457",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",

View File

@ -89,7 +89,9 @@ def get_sle_conditions(filters):
conditions.append("""sle.item_code in (select item.name from tabItem item
{item_conditions})""".format(item_conditions=item_conditions))
if filters.get("warehouse"):
conditions.append(get_warehouse_condition(filters.get("warehouse")))
warehouse_condition = get_warehouse_condition(filters.get("warehouse"))
if warehouse_condition:
conditions.append(warehouse_condition)
if filters.get("voucher_no"):
conditions.append("voucher_no=%(voucher_no)s")
if filters.get("batch_no"):

View File

@ -60,32 +60,12 @@ erpnext/stock/doctype/material_request/tests/test_material_request_type_manufact
erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js
erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js
erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js
erpnext/schools/doctype/grading_scale/test_grading_scale.js
erpnext/schools/doctype/assessment_criteria_group/test_assessment_criteria_group.js
erpnext/schools/doctype/assessment_criteria/test_assessment_criteria.js
erpnext/schools/doctype/course/test_course.js
erpnext/schools/doctype/program/test_program.js
erpnext/hr/doctype/salary_structure/test_salary_structure.js
erpnext/hr/doctype/salary_slip/test_salary_slip.js
erpnext/hr/doctype/process_payroll/test_process_payroll.js
erpnext/hr/doctype/job_opening/test_job_opening.js
erpnext/hr/doctype/job_applicant/test_job_applicant.js
erpnext/hr/doctype/offer_letter/test_offer_letter.js
erpnext/schools/doctype/guardian/test_guardian.js
erpnext/schools/doctype/student_admission/test_student_admission.js
erpnext/schools/doctype/student_applicant/tests/test_student_applicant_dummy_data.js
erpnext/schools/doctype/student_applicant/tests/test_student_applicant.js
erpnext/schools/doctype/student_applicant/tests/test_student_applicant_options.js
erpnext/schools/doctype/student_log/test_student_log.js
erpnext/schools/doctype/student_group/test_student_group.js
erpnext/schools/doctype/student_group_creation_tool/test_student_group_creation_tool.js
erpnext/schools/doctype/student_leave_application/test_student_leave_application.js
erpnext/schools/doctype/student_attendance_tool/test_student_attendance_tool.js
erpnext/schools/doctype/student_attendance/test_student_attendance.js
erpnext/schools/doctype/assessment_group/test_assessment_group.js
erpnext/schools/doctype/assessment_plan/test_assessment_plan.js
erpnext/schools/doctype/assessment_result/test_assessment_result.js
erpnext/schools/doctype/assessment_result_tool/test_assessment_result_tool.js
erpnext/buying/doctype/supplier/test_supplier.js
erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js
erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js
@ -101,4 +81,24 @@ erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_d
erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js
erpnext/buying/doctype/purchase_order/tests/test_purchase_order_receipt.js
erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.js
erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js
erpnext/schools/doctype/grading_scale/test_grading_scale.js
erpnext/schools/doctype/assessment_criteria_group/test_assessment_criteria_group.js
erpnext/schools/doctype/assessment_criteria/test_assessment_criteria.js
erpnext/schools/doctype/course/test_course.js
erpnext/schools/doctype/program/test_program.js
erpnext/schools/doctype/guardian/test_guardian.js
erpnext/schools/doctype/student_admission/test_student_admission.js
erpnext/schools/doctype/student_applicant/tests/test_student_applicant_dummy_data.js
erpnext/schools/doctype/student_applicant/tests/test_student_applicant.js
erpnext/schools/doctype/student_applicant/tests/test_student_applicant_options.js
erpnext/schools/doctype/student_log/test_student_log.js
erpnext/schools/doctype/student_group/test_student_group.js
erpnext/schools/doctype/student_group_creation_tool/test_student_group_creation_tool.js
erpnext/schools/doctype/student_leave_application/test_student_leave_application.js
erpnext/schools/doctype/student_attendance_tool/test_student_attendance_tool.js
erpnext/schools/doctype/student_attendance/test_student_attendance.js
erpnext/schools/doctype/assessment_group/test_assessment_group.js
erpnext/schools/doctype/assessment_plan/test_assessment_plan.js
erpnext/schools/doctype/assessment_result/test_assessment_result.js
erpnext/schools/doctype/assessment_result_tool/test_assessment_result_tool.js
erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js