Merge branch 'staging-fixes' into pplan_fix

This commit is contained in:
Sagar Vora 2019-01-07 15:54:16 +05:30 committed by GitHub
commit 7d60c03ed9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
96 changed files with 2726 additions and 1738 deletions

View File

@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
__version__ = '10.1.74'
__version__ = '10.1.76'
def get_default_company(user=None):
'''Get default company for user'''

View File

@ -37,8 +37,8 @@ frappe.ui.form.on('POS Profile', {
return { filters: { doc_type: "Sales Invoice", print_format_type: "Js"} };
});
frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'use_pos_in_offline_mode', (r) => {
is_offline = r && cint(r.use_pos_in_offline_mode)
frappe.db.get_value('POS Settings', 'POS Settings', 'use_pos_in_offline_mode', (r) => {
const is_offline = r && cint(r.use_pos_in_offline_mode)
frm.toggle_display('offline_pos_section', is_offline);
frm.toggle_display('print_format_for_online', !is_offline);
});

View File

@ -4,7 +4,7 @@
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 1,
"autoname": "field:pos_profile_name",
"autoname": "Prompt",
"beta": 0,
"creation": "2013-05-24 12:15:51",
"custom": 0,
@ -52,6 +52,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "section_break_2",
"fieldtype": "Section Break",
"hidden": 0,
@ -76,38 +77,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "pos_profile_name",
"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": "POS Profile Name",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -142,6 +111,240 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "customer",
"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": "Customer",
"length": 0,
"no_copy": 0,
"oldfieldname": "customer_account",
"oldfieldtype": "Link",
"options": "Customer",
"permlevel": 0,
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"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": "Company",
"length": 0,
"no_copy": 0,
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 1,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "company.country",
"fieldname": "country",
"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": "Country",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "update_stock",
"fieldname": "warehouse",
"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": "Warehouse",
"length": 0,
"no_copy": 0,
"oldfieldname": "warehouse",
"oldfieldtype": "Link",
"options": "Warehouse",
"permlevel": 0,
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "campaign",
"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": "Campaign",
"length": 0,
"no_copy": 0,
"options": "Campaign",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company_address",
"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 Address",
"length": 0,
"no_copy": 0,
"options": "Address",
"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_in_quick_entry": 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_in_quick_entry": 0,
@ -374,207 +577,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "customer",
"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": "Customer",
"length": 0,
"no_copy": 0,
"oldfieldname": "customer_account",
"oldfieldtype": "Link",
"options": "Customer",
"permlevel": 0,
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"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": "Company",
"length": 0,
"no_copy": 0,
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 1,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "company.country",
"fieldname": "country",
"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": "Country",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "update_stock",
"fieldname": "warehouse",
"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": "Warehouse",
"length": 0,
"no_copy": 0,
"oldfieldname": "warehouse",
"oldfieldtype": "Link",
"options": "Warehouse",
"permlevel": 0,
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "campaign",
"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": "Campaign",
"length": 0,
"no_copy": 0,
"options": "Campaign",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "section_break_15",
"fieldtype": "Section Break",
"hidden": 0,
@ -649,6 +652,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Mode of Payment",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -714,6 +718,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -736,6 +741,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Only show Items from these Item Groups",
"fieldname": "item_groups",
"fieldtype": "Table",
"hidden": 0,
@ -800,6 +806,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Only show Customer of these Customer Groups",
"fieldname": "customer_groups",
"fieldtype": "Table",
"hidden": 0,
@ -843,6 +850,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Print Settings",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -925,40 +933,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "select_print_heading",
"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": "Print Heading",
"length": 0,
"no_copy": 0,
"oldfieldname": "select_print_heading",
"oldfieldtype": "Select",
"options": "Print Heading",
"permlevel": 0,
"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_in_quick_entry": 0,
@ -990,40 +964,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "selling_price_list",
"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": "Price List",
"length": 0,
"no_copy": 0,
"oldfieldname": "price_list_name",
"oldfieldtype": "Select",
"options": "Price List",
"permlevel": 0,
"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_in_quick_entry": 0,
@ -1061,110 +1001,11 @@
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "apply_discount",
"fieldtype": "Check",
"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": "Apply Discount",
"length": 0,
"no_copy": 0,
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Grand Total",
"depends_on": "",
"fieldname": "apply_discount_on",
"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": "Apply Discount On",
"length": 0,
"no_copy": 0,
"options": "Grand Total\nNet Total",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company_address_section",
"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": "Company Address",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company_address",
"fieldname": "select_print_heading",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
@ -1173,12 +1014,13 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Company Address Name",
"label": "Print Heading",
"length": 0,
"no_copy": 0,
"options": "Address",
"oldfieldname": "select_print_heading",
"oldfieldtype": "Select",
"options": "Print Heading",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@ -1207,7 +1049,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Offline POS Section",
"label": "Offline POS Settings",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -1389,6 +1231,40 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "selling_price_list",
"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": "Price List",
"length": 0,
"no_copy": 0,
"oldfieldname": "price_list_name",
"oldfieldtype": "Select",
"options": "Price List",
"permlevel": 0,
"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_in_quick_entry": 0,
@ -1688,6 +1564,41 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Grand Total",
"depends_on": "",
"fieldname": "apply_discount_on",
"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": "Apply Discount On",
"length": 0,
"no_copy": 0,
"options": "Grand Total\nNet Total",
"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
}
],
"has_web_view": 0,
@ -1701,7 +1612,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-12-03 14:16:08.589778",
"modified": "2018-12-13 13:36:22.045519",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Profile",
@ -1749,11 +1660,11 @@
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"search_fields": "pos_profile_name",
"search_fields": "",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "pos_profile_name",
"title_field": "",
"track_changes": 0,
"track_seen": 0,
"track_views": 0

View File

@ -127,25 +127,26 @@ def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
'txt': '%%%s%%' % txt
}
pos_profile = frappe.db.sql("""select pf.name, pf.pos_profile_name
pos_profile = frappe.db.sql("""select pf.name
from
`tabPOS Profile` pf, `tabPOS Profile User` pfu
where
pfu.parent = pf.name and pfu.user = %(user)s and pf.company = %(company)s
and (pf.name like %(txt)s or pf.pos_profile_name like %(txt)s)
and (pf.name like %(txt)s)
and pf.disabled = 0 limit %(start)s, %(page_len)s""", args)
if not pos_profile:
del args['user']
pos_profile = frappe.db.sql("""select pf.name, pf.pos_profile_name
pos_profile = frappe.db.sql("""select pf.name
from
`tabPOS Profile` pf left join `tabPOS Profile User` pfu
on
pf.name = pfu.parent
where
ifnull(pfu.user, '') = '' and pf.company = %(company)s and
(pf.name like %(txt)s or pf.pos_profile_name like %(txt)s)
ifnull(pfu.user, '') = ''
and pf.company = %(company)s
and pf.name like %(txt)s
and pf.disabled = 0""", args)
return pos_profile

View File

@ -40,7 +40,6 @@ def make_pos_profile():
"expense_account": "_Test Account Cost for Goods Sold - _TC",
"income_account": "Sales - _TC",
"name": "_Test POS Profile",
"pos_profile_name": "_Test POS Profile",
"naming_series": "_T-POS Profile-",
"selling_price_list": "_Test Price List",
"territory": "_Test Territory",

View File

@ -830,6 +830,10 @@ class PurchaseInvoice(BuyingController):
return
tax_withholding_details = get_party_tax_withholding_details(self)
if not tax_withholding_details:
return
accounts = []
for d in self.taxes:
if d.account_head == tax_withholding_details.get("account_head"):
@ -839,6 +843,12 @@ class PurchaseInvoice(BuyingController):
if not accounts or tax_withholding_details.get("account_head") not in accounts:
self.append("taxes", tax_withholding_details)
to_remove = [d for d in self.taxes
if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")]
for d in to_remove:
self.remove(d)
# calculate totals again after applying TDS
self.calculate_taxes_and_totals()

View File

@ -324,7 +324,8 @@ class SalesInvoice(SellingController):
return {
"print_format": print_format,
"allow_edit_rate": pos.get("allow_user_to_edit_rate"),
"allow_edit_discount": pos.get("allow_user_to_edit_discount")
"allow_edit_discount": pos.get("allow_user_to_edit_discount"),
"campaign": pos.get("campaign")
}
def update_time_sheet(self, sales_invoice):

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
@ -375,7 +375,7 @@ class TestSalesInvoice(unittest.TestCase):
si.insert()
self.assertEqual(si.net_total, 4600)
self.assertEqual(si.get("taxes")[0].tax_amount, 874.0)
self.assertEqual(si.get("taxes")[0].total, 5474.0)
@ -405,12 +405,12 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(si.total, 975)
self.assertEqual(si.net_total, 900)
self.assertEqual(si.get("taxes")[0].tax_amount, 216.0)
self.assertEqual(si.get("taxes")[0].total, 1116.0)
self.assertEqual(si.grand_total, 1116.0)
def test_inclusive_rate_validations(self):
si = frappe.copy_doc(test_records[2])
for i, tax in enumerate(si.get("taxes")):
@ -552,7 +552,7 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(si.grand_total, 1215.90)
self.assertEqual(si.rounding_adjustment, 0.01)
self.assertEqual(si.base_rounding_adjustment, 0.50)
def test_outstanding(self):
w = self.make()
@ -762,6 +762,20 @@ class TestSalesInvoice(unittest.TestCase):
set_perpetual_inventory(0)
frappe.db.sql("delete from `tabPOS Profile`")
def test_pos_si_without_payment(self):
set_perpetual_inventory()
make_pos_profile()
pos = copy.deepcopy(test_records[1])
pos["is_pos"] = 1
pos["update_stock"] = 1
si = frappe.copy_doc(pos)
si.insert()
# Check that the invoice cannot be submitted without payments
self.assertRaises(frappe.ValidationError, si.submit)
def test_sales_invoice_gl_entry_with_perpetual_inventory_no_item_code(self):
set_perpetual_inventory()
@ -923,7 +937,7 @@ class TestSalesInvoice(unittest.TestCase):
self.assertRaises(SerialNoWarehouseError, si.submit)
def test_serial_numbers_against_delivery_note(self):
"""
"""
check if the sales invoice item serial numbers and the delivery note items
serial numbers are same
"""
@ -1238,7 +1252,7 @@ class TestSalesInvoice(unittest.TestCase):
def test_item_wise_tax_breakup_india(self):
frappe.flags.country = "India"
si = self.create_si_to_test_tax_breakup()
itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(si)
@ -1256,12 +1270,12 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(itemised_tax, expected_itemised_tax)
self.assertEqual(itemised_taxable_amount, expected_itemised_taxable_amount)
frappe.flags.country = None
def test_item_wise_tax_breakup_outside_india(self):
frappe.flags.country = "United States"
si = self.create_si_to_test_tax_breakup()
itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(si)
@ -1287,7 +1301,7 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(itemised_tax, expected_itemised_tax)
self.assertEqual(itemised_taxable_amount, expected_itemised_taxable_amount)
frappe.flags.country = None
def create_si_to_test_tax_breakup(self):
@ -1375,7 +1389,7 @@ class TestSalesInvoice(unittest.TestCase):
shipping_rule = create_shipping_rule(shipping_rule_type = "Selling", shipping_rule_name = "Shipping Rule - Sales Invoice Test")
si = frappe.copy_doc(test_records[2])
si.shipping_rule = shipping_rule.name
si.insert()
@ -1392,14 +1406,14 @@ class TestSalesInvoice(unittest.TestCase):
"cost_center": shipping_rule.cost_center,
"tax_amount": shipping_amount,
"description": shipping_rule.name
}
}
si.append("taxes", shipping_charge)
si.save()
self.assertEqual(si.net_total, 1250)
self.assertEqual(si.total_taxes_and_charges, 577.05)
self.assertEqual(si.grand_total, 1827.05)
self.assertEqual(si.grand_total, 1827.05)
def test_create_invoice_without_terms(self):
si = create_sales_invoice(do_not_save=1)
@ -1496,7 +1510,7 @@ class TestSalesInvoice(unittest.TestCase):
for gle in gl_entries:
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
accounts_settings.save()
@ -1524,9 +1538,9 @@ def create_sales_invoice(**args):
"warehouse": args.warehouse or "_Test Warehouse - _TC",
"qty": args.qty or 1,
"rate": args.rate or 100,
"income_account": "Sales - _TC",
"expense_account": "Cost of Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
"income_account": args.income_account or "Sales - _TC",
"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
"cost_center": args.cost_center or "_Test Cost Center - _TC",
"serial_no": args.serial_no
})

View File

@ -24,6 +24,7 @@ def get_party_tax_withholding_details(ref_doc):
.format(tax_withholding_category, ref_doc.company))
tds_amount = get_tds_amount(ref_doc, tax_details, fy)
tax_row = get_tax_row(tax_details, tds_amount)
return tax_row
def get_tax_withholding_details(tax_withholding_category, fiscal_year, company):
@ -62,46 +63,64 @@ def get_tax_row(tax_details, tds_amount):
def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
fiscal_year, year_start_date, year_end_date = fiscal_year_details
tds_amount = 0
tds_deducted = 0
def _get_tds():
tds_amount = 0
if not tax_details.threshold or ref_doc.net_total >= tax_details.threshold:
tds_amount = ref_doc.net_total * tax_details.rate / 100
return tds_amount
def _get_tds(amount):
if amount <= 0:
return 0
if tax_details.cumulative_threshold:
entries = frappe.db.sql("""
return amount * tax_details.rate / 100
entries = frappe.db.sql("""
select voucher_no, credit
from `tabGL Entry`
where party=%s and fiscal_year=%s and credit > 0
""", (ref_doc.supplier, fiscal_year), as_dict=1)
supplier_credit_amount = flt(sum([d.credit for d in entries]))
vouchers = [d.voucher_no for d in entries]
advance_vouchers = get_advance_vouchers(ref_doc.supplier, fiscal_year)
vouchers = [d.voucher_no for d in entries]
vouchers += get_advance_vouchers(ref_doc.supplier, fiscal_year)
tds_vouchers = vouchers + advance_vouchers
tds_deducted = 0
if vouchers:
tds_deducted = flt(frappe.db.sql("""
select sum(credit)
from `tabGL Entry`
where account=%s and fiscal_year=%s and credit > 0
and voucher_no in ({0})
""".format(', '.join(["'%s'" % d for d in vouchers])),
(tax_details.account_head, fiscal_year))[0][0])
if tds_vouchers:
tds_deducted = frappe.db.sql("""
SELECT sum(credit) FROM `tabGL Entry`
WHERE
account=%s and fiscal_year=%s and credit > 0
and voucher_no in ({0})""". format(','.join(['%s'] * len(tds_vouchers))),
((tax_details.account_head, fiscal_year) + tuple(tds_vouchers)))
tds_deducted = tds_deducted[0][0] if tds_deducted and tds_deducted[0][0] else 0
if tds_deducted:
tds_amount = _get_tds(ref_doc.net_total)
else:
supplier_credit_amount = frappe.get_all('Purchase Invoice Item',
fields = ['sum(net_amount)'],
filters = {'parent': ('in', vouchers), 'docstatus': 1}, as_list=1)
supplier_credit_amount = (supplier_credit_amount[0][0]
if supplier_credit_amount and supplier_credit_amount[0][0] else 0)
jv_supplier_credit_amt = frappe.get_all('Journal Entry Account',
fields = ['sum(credit_in_account_currency)'],
filters = {
'parent': ('in', vouchers), 'docstatus': 1,
'party': ref_doc.supplier,
'reference_type': ('not in', ['Purchase Invoice'])
}, as_list=1)
supplier_credit_amount += (jv_supplier_credit_amt[0][0]
if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0)
supplier_credit_amount += ref_doc.net_total
debit_note_amount = get_debit_note_amount(ref_doc.supplier, year_start_date, year_end_date)
supplier_credit_amount -= debit_note_amount
total_invoiced_amount = supplier_credit_amount + tds_deducted \
+ flt(ref_doc.net_total) - debit_note_amount
if total_invoiced_amount >= tax_details.cumulative_threshold:
total_applicable_tds = total_invoiced_amount * tax_details.rate / 100
tds_amount = min(total_applicable_tds - tds_deducted, ref_doc.net_total)
else:
tds_amount = _get_tds()
else:
tds_amount = _get_tds()
if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold)
or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
tds_amount = _get_tds(supplier_credit_amount)
return tds_amount
@ -114,7 +133,7 @@ def get_advance_vouchers(supplier, fiscal_year=None, company=None, from_date=Non
select distinct voucher_no
from `tabGL Entry`
where party=%s and %s and debit > 0
""", (supplier, condition))
""", (supplier, condition)) or []
def get_debit_note_amount(supplier, year_start_date, year_end_date, company=None):
condition = ""
@ -126,4 +145,4 @@ def get_debit_note_amount(supplier, year_start_date, year_end_date, company=None
from `tabPurchase Invoice`
where supplier=%s %s and is_return=1 and docstatus=1
and posting_date between %s and %s
""", (supplier, condition, year_start_date, year_end_date)))
""", (supplier, condition, year_start_date, year_end_date)))

View File

@ -6,6 +6,7 @@ from __future__ import unicode_literals
import frappe
import unittest
from frappe.utils import today
from erpnext.accounts.utils import get_fiscal_year
test_dependencies = ["Supplier Group"]
@ -14,65 +15,105 @@ class TestTaxWithholdingCategory(unittest.TestCase):
def setUpClass(self):
# create relevant supplier, etc
create_records()
create_tax_with_holding_category()
def test_single_threshold_tds(self):
frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "TDS - 194D - Individual")
pi = create_purchase_invoice()
def test_cumulative_threshold_tds(self):
frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "Cumulative Threshold TDS")
invoices = []
# create invoices for lower than single threshold tax rate
for _ in xrange(2):
pi = create_purchase_invoice(supplier = "Test TDS Supplier")
pi.submit()
invoices.append(pi)
# create another invoice whose total when added to previously created invoice,
# surpasses cumulative threshhold
pi = create_purchase_invoice(supplier = "Test TDS Supplier")
pi.submit()
self.assertEqual(pi.taxes_and_charges_deducted, 800)
self.assertEqual(pi.grand_total, 15200)
# assert equal tax deduction on total invoice amount uptil now
self.assertEqual(pi.taxes_and_charges_deducted, 3000)
self.assertEqual(pi.grand_total, 7000)
invoices.append(pi)
# TDS is already deducted, so from onward system will deduct the TDS on every invoice
pi = create_purchase_invoice(supplier = "Test TDS Supplier", rate=5000)
pi.submit()
# assert equal tax deduction on total invoice amount uptil now
self.assertEqual(pi.taxes_and_charges_deducted, 500)
invoices.append(pi)
#delete invoices to avoid clashing
for d in invoices:
d.cancel()
frappe.delete_doc("Purchase Invoice", d.name)
def test_single_threshold_tds(self):
invoices = []
frappe.db.set_value("Supplier", "Test TDS Supplier1", "tax_withholding_category", "Single Threshold TDS")
pi = create_purchase_invoice(supplier = "Test TDS Supplier1", rate = 20000)
pi.submit()
invoices.append(pi)
self.assertEqual(pi.taxes_and_charges_deducted, 2000)
self.assertEqual(pi.grand_total, 18000)
# check gl entry for the purchase invoice
gl_entries = frappe.db.get_all('GL Entry', filters={'voucher_no': pi.name}, fields=["*"])
self.assertEqual(len(gl_entries), 3)
for d in gl_entries:
if d.account == pi.credit_to:
self.assertEqual(d.credit, 15200)
self.assertEqual(d.credit, 18000)
elif d.account == pi.items[0].get("expense_account"):
self.assertEqual(d.debit, 16000)
self.assertEqual(d.debit, 20000)
elif d.account == pi.taxes[0].get("account_head"):
self.assertEqual(d.credit, 800)
self.assertEqual(d.credit, 2000)
else:
raise ValueError("Account head does not match.")
# delete purchase invoice to avoid it interefering in other tests
pi.cancel()
frappe.delete_doc('Purchase Invoice', pi.name)
def test_cumulative_threshold_tds(self):
frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "TDS - 194C - Individual")
invoices = []
# create invoices for lower than single threshold tax rate
for _ in xrange(6):
pi = create_purchase_invoice()
pi.submit()
invoices.append(pi)
# create another invoice whose total when added to previously created invoice,
# surpasses cumulative threshhold
pi = create_purchase_invoice()
pi = create_purchase_invoice(supplier = "Test TDS Supplier1")
pi.submit()
# assert equal tax deduction on total invoice amount uptil now
self.assertEqual(pi.taxes_and_charges_deducted, 1120)
self.assertEqual(pi.grand_total, 14880)
invoices.append(pi)
# TDS amount is 1000 because in previous invoices it's already deducted
self.assertEqual(pi.taxes_and_charges_deducted, 1000)
# delete invoices to avoid clashing
for d in invoices:
d.cancel()
frappe.delete_doc("Purchase Invoice", d.name)
def create_purchase_invoice(qty=1):
def test_single_threshold_tds_with_previous_vouchers(self):
invoices = []
frappe.db.set_value("Supplier", "Test TDS Supplier2", "tax_withholding_category", "Single Threshold TDS")
pi = create_purchase_invoice(supplier="Test TDS Supplier2")
pi.submit()
invoices.append(pi)
pi = create_purchase_invoice(supplier="Test TDS Supplier2")
pi.submit()
invoices.append(pi)
self.assertEqual(pi.taxes_and_charges_deducted, 2000)
self.assertEqual(pi.grand_total, 8000)
# delete invoices to avoid clashing
for d in invoices:
d.cancel()
frappe.delete_doc("Purchase Invoice", d.name)
def create_purchase_invoice(**args):
# return sales invoice doc object
item = frappe.get_doc('Item', {'item_name': 'TDS Item'})
args = frappe._dict(args)
pi = frappe.get_doc({
"doctype": "Purchase Invoice",
"posting_date": today(),
"apply_tds": 1,
"supplier": frappe.get_doc('Supplier', {"supplier_name": "Test TDS Supplier"}).name,
"supplier": args.supplier,
"company": '_Test Company',
"taxes_and_charges": "",
"currency": "INR",
@ -81,8 +122,8 @@ def create_purchase_invoice(qty=1):
"items": [{
'doctype': 'Purchase Invoice Item',
'item_code': item.name,
'qty': qty,
'rate': 16000,
'qty': args.qty or 1,
'rate': args.rate or 10000,
'cost_center': 'Main - _TC',
'expense_account': 'Stock Received But Not Billed - _TC'
}]
@ -92,20 +133,73 @@ def create_purchase_invoice(qty=1):
return pi
def create_records():
# create a new supplier
frappe.get_doc({
"supplier_group": "_Test Supplier Group",
"supplier_name": "Test TDS Supplier",
"doctype": "Supplier",
"tax_withholding_category": "TDS - 194D - Individual"
}).insert()
# create a new suppliers
for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']:
if frappe.db.exists('Supplier', name):
continue
frappe.get_doc({
"supplier_group": "_Test Supplier Group",
"supplier_name": name,
"doctype": "Supplier",
}).insert()
# create an item
frappe.get_doc({
"doctype": "Item",
"item_code": "TDS Item",
"item_name": "TDS Item",
"item_group": "All Item Groups",
"company": "_Test Company",
"is_stock_item": 0,
}).insert()
if not frappe.db.exists('Item', "TDS Item"):
frappe.get_doc({
"doctype": "Item",
"item_code": "TDS Item",
"item_name": "TDS Item",
"item_group": "All Item Groups",
"is_stock_item": 0,
}).insert()
# create an account
if not frappe.db.exists("Account", "TDS - _TC"):
frappe.get_doc({
'doctype': 'Account',
'company': '_Test Company',
'account_name': 'TDS',
'parent_account': 'Tax Assets - _TC',
'report_type': 'Balance Sheet',
'root_type': 'Asset'
}).insert()
def create_tax_with_holding_category():
fiscal_year = get_fiscal_year(today(), company="_Test Company")[0]
# Cummulative thresold
if not frappe.db.exists("Tax Withholding Category", "Cumulative Threshold TDS"):
frappe.get_doc({
"doctype": "Tax Withholding Category",
"name": "Cumulative Threshold TDS",
"category_name": "10% TDS",
"rates": [{
'fiscal_year': fiscal_year,
'tax_withholding_rate': 10,
'single_threshold': 0,
'cumulative_threshold': 30000.00
}],
"accounts": [{
'company': '_Test Company',
'account': 'TDS - _TC'
}]
}).insert()
# Single thresold
if not frappe.db.exists("Tax Withholding Category", "Single Threshold TDS"):
frappe.get_doc({
"doctype": "Tax Withholding Category",
"name": "Single Threshold TDS",
"category_name": "10% TDS",
"rates": [{
'fiscal_year': fiscal_year,
'tax_withholding_rate': 10,
'single_threshold': 20000.00,
'cumulative_threshold': 0
}],
"accounts": [{
'company': '_Test Company',
'account': 'TDS - _TC'
}]
}).insert()

View File

@ -1135,16 +1135,18 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
},
apply_category: function() {
var me = this;
category = this.selected_item_group || "All Item Groups";
frappe.db.get_value("Item Group", {lft: 1, is_group: 1}, "name", (r) => {
category = this.selected_item_group || r.name;
if(category == 'All Item Groups') {
return this.item_data
} else {
return this.item_data.filter(function(element, index, array){
return element.item_group == category;
});
}
if(category == r.name) {
return this.item_data
} else {
return this.item_data.filter(function(element, index, array){
return element.item_group == category;
});
}
})
},
bind_items_event: function() {

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe, erpnext
from frappe import _, msgprint, scrub
from frappe.defaults import get_user_permissions
from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
from frappe.model.utils import get_fetch_values
from frappe.utils import (add_days, getdate, formatdate, date_diff,
add_years, get_timestamp, nowdate, flt, add_months, get_last_day)
@ -151,10 +151,7 @@ def get_default_price_list(party):
def set_price_list(out, party, party_type, given_price_list):
# price list
price_list = filter(None, get_user_permissions()
.get("Price List", {})
.get("docs", []))
price_list = list(price_list)
price_list = get_permitted_documents('Price List')
if price_list:
price_list = price_list[0]

View File

@ -107,6 +107,11 @@ frappe.query_reports["Accounts Receivable"] = {
"label": __("Show PDC in Print"),
"fieldtype": "Check",
},
{
"fieldname":"based_on_payment_terms",
"label": __("Based On Payment Terms"),
"fieldtype": "Check",
},
{
"fieldname":"tax_id",
"label": __("Tax Id"),

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe, erpnext
from frappe import _, scrub
from frappe.utils import getdate, nowdate, flt, cint
from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr
class ReceivablePayableReport(object):
def __init__(self, filters=None):
@ -57,6 +57,21 @@ class ReceivablePayableReport(object):
credit_or_debit_note = "Credit Note" if args.get("party_type") == "Customer" else "Debit Note"
if self.filters.based_on_payment_terms:
columns.append({
"label": "Payment Term",
"fieldname": "payment_term",
"fieldtype": "Data",
"width": 120
})
columns.append({
"label": "Invoice Grand Total",
"fieldname": "invoice_grand_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120
})
for label in ("Invoiced Amount", "Paid Amount", credit_or_debit_note, "Outstanding Amount"):
columns.append({
"label": label,
@ -97,12 +112,6 @@ class ReceivablePayableReport(object):
"options": "Currency",
"width": 100
},
{
"fieldname": "pdc/lc_date",
"label": _("PDC/LC Date"),
"fieldtype": "Date",
"width": 110
},
{
"fieldname": "pdc/lc_ref",
"label": _("PDC/LC Ref"),
@ -113,14 +122,14 @@ class ReceivablePayableReport(object):
"fieldname": "pdc/lc_amount",
"label": _("PDC/LC Amount"),
"fieldtype": "Currency",
"options": "Currency",
"options": "currency",
"width": 130
},
{
"fieldname": "remaining_balance",
"label": _("Remaining Balance"),
"fieldtype": "Currency",
"options": "Currency",
"options": "currency",
"width": 130
}]
@ -151,108 +160,203 @@ class ReceivablePayableReport(object):
def get_data(self, party_naming_by, args):
from erpnext.accounts.utils import get_currency_precision
currency_precision = get_currency_precision() or 2
dr_or_cr = "debit" if args.get("party_type") == "Customer" else "credit"
self.currency_precision = get_currency_precision() or 2
self.dr_or_cr = "debit" if args.get("party_type") == "Customer" else "credit"
future_vouchers = self.get_entries_after(self.filters.report_date, args.get("party_type"))
if not self.filters.get("company"):
self.filters["company"] = frappe.db.get_single_value('Global Defaults', 'default_company')
company_currency = frappe.get_cached_value('Company', self.filters.get("company"), "default_currency")
self.company_currency = frappe.get_cached_value('Company', self.filters.get("company"), "default_currency")
return_entries = self.get_return_entries(args.get("party_type"))
data = []
pdc_details = get_pdc_details(args.get("party_type"), self.filters.report_date)
self.pdc_details = get_pdc_details(args.get("party_type"), self.filters.report_date)
gl_entries_data = self.get_entries_till(self.filters.report_date, args.get("party_type"))
if gl_entries_data:
voucher_nos = [d.voucher_no for d in gl_entries_data] or []
dn_details = get_dn_details(args.get("party_type"), voucher_nos)
voucher_details = get_voucher_details(args.get("party_type"), voucher_nos, dn_details)
self.voucher_details = get_voucher_details(args.get("party_type"), voucher_nos, dn_details)
if self.filters.based_on_payment_terms:
self.payment_term_map = self.get_payment_term_detail(voucher_nos)
for gle in gl_entries_data:
if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers):
outstanding_amount, credit_note_amount = self.get_outstanding_amount(gle,
self.filters.report_date, dr_or_cr, return_entries, currency_precision)
if abs(outstanding_amount) > 0.1/10**currency_precision:
row = [gle.posting_date, gle.party]
if self.is_receivable_or_payable(gle, self.dr_or_cr, future_vouchers):
outstanding_amount, credit_note_amount, payment_amount = self.get_outstanding_amount(
gle,self.filters.report_date, self.dr_or_cr, return_entries)
# customer / supplier name
if party_naming_by == "Naming Series":
row += [self.get_party_name(gle.party_type, gle.party)]
temp_outstanding_amt = outstanding_amount
temp_credit_note_amt = credit_note_amount
# get due date
due_date = voucher_details.get(gle.voucher_no, {}).get("due_date", "")
bill_date = voucher_details.get(gle.voucher_no, {}).get("bill_date", "")
if abs(outstanding_amount) > 0.1/10**self.currency_precision:
if self.filters.based_on_payment_terms and self.payment_term_map.get(gle.voucher_no):
for d in self.payment_term_map.get(gle.voucher_no):
# Allocate payment amount based on payment terms(FIFO order)
payment_amount, d.payment_amount = self.allocate_based_on_fifo(payment_amount, d.payment_term_amount)
row += [gle.voucher_type, gle.voucher_no, due_date]
term_outstanding_amount = d.payment_term_amount - d.payment_amount
# get supplier bill details
if args.get("party_type") == "Supplier":
row += [
voucher_details.get(gle.voucher_no, {}).get("bill_no", ""),
voucher_details.get(gle.voucher_no, {}).get("bill_date", "")
]
# Allocate credit note based on payment terms(FIFO order)
credit_note_amount, d.credit_note_amount = self.allocate_based_on_fifo(credit_note_amount, term_outstanding_amount)
# invoiced and paid amounts
invoiced_amount = gle.get(dr_or_cr) if (gle.get(dr_or_cr) > 0) else 0
paid_amt = invoiced_amount - outstanding_amount - credit_note_amount
row += [invoiced_amount, paid_amt, credit_note_amount, outstanding_amount]
term_outstanding_amount -= d.credit_note_amount
row_outstanding = term_outstanding_amount
# Allocate PDC based on payment terms(FIFO order)
d.pdc_details, d.pdc_amount = self.allocate_pdc_amount_in_fifo(gle, row_outstanding)
if term_outstanding_amount > 0:
row = self.prepare_row(party_naming_by, args, gle, term_outstanding_amount,
d.credit_note_amount, d.due_date, d.payment_amount , d.payment_term_amount,
d.description, d.pdc_amount, d.pdc_details)
data.append(row)
if credit_note_amount:
row = self.prepare_row_without_payment_terms(party_naming_by, args, gle, temp_outstanding_amt,
temp_credit_note_amt)
data.append(row)
# ageing data
if self.filters.ageing_based_on == "Due Date":
entry_date = due_date
elif self.filters.ageing_based_on == "Supplier Invoice Date":
entry_date = bill_date
else:
entry_date = gle.posting_date
row += get_ageing_data(cint(self.filters.range1), cint(self.filters.range2),
cint(self.filters.range3), self.age_as_on, entry_date, outstanding_amount)
# issue 6371-Ageing buckets should not have amounts if due date is not reached
if self.filters.ageing_based_on == "Due Date" \
and getdate(due_date) > getdate(self.filters.report_date):
row[-1]=row[-2]=row[-3]=row[-4]=0
if self.filters.ageing_based_on == "Supplier Invoice Date" \
and getdate(bill_date) > getdate(self.filters.report_date):
row[-1]=row[-2]=row[-3]=row[-4]=0
if self.filters.get(scrub(args.get("party_type"))):
row.append(gle.account_currency)
else:
row.append(company_currency)
pdc = pdc_details.get((gle.voucher_no, gle.party), {})
remaining_balance = outstanding_amount - flt(pdc.get("pdc_amount"))
row += [pdc.get("pdc_date"), pdc.get("pdc_ref"),
flt(pdc.get("pdc_amount")), remaining_balance]
if args.get('party_type') == 'Customer':
# customer LPO
row += [voucher_details.get(gle.voucher_no, {}).get("po_no")]
# Delivery Note
row += [voucher_details.get(gle.voucher_no, {}).get("delivery_note")]
# customer territory / supplier group
if args.get("party_type") == "Customer":
row += [self.get_territory(gle.party), self.get_customer_group(gle.party),
voucher_details.get(gle.voucher_no, {}).get("sales_person")]
if args.get("party_type") == "Supplier":
row += [self.get_supplier_group(gle.party)]
row.append(gle.remarks)
data.append(row)
row = self.prepare_row_without_payment_terms(party_naming_by, args, gle, outstanding_amount,
credit_note_amount)
data.append(row)
return data
def allocate_pdc_amount_in_fifo(self, gle, row_outstanding):
pdc_list = self.pdc_details.get((gle.voucher_no, gle.party), [])
pdc_details = []
pdc_amount = 0
for pdc in pdc_list:
if row_outstanding <= pdc.pdc_amount:
pdc_amount += row_outstanding
pdc.pdc_amount -= row_outstanding
if row_outstanding and pdc.pdc_ref and pdc.pdc_date:
pdc_details.append(cstr(pdc.pdc_ref) + "/" + formatdate(pdc.pdc_date))
row_outstanding = 0
else:
pdc_amount = pdc.pdc_amount
if pdc.pdc_amount and pdc.pdc_ref and pdc.pdc_date:
pdc_details.append(cstr(pdc.pdc_ref) + "/" + formatdate(pdc.pdc_date))
pdc.pdc_amount = 0
row_outstanding -= pdc_amount
return pdc_details, pdc_amount
def prepare_row_without_payment_terms(self, party_naming_by, args, gle, outstanding_amount, credit_note_amount):
pdc_list = self.pdc_details.get((gle.voucher_no, gle.party), [])
pdc_amount = 0
pdc_details = []
for d in pdc_list:
pdc_amount += flt(d.pdc_amount)
if pdc_amount and d.pdc_ref and d.pdc_date:
pdc_details.append(cstr(d.pdc_ref) + "/" + formatdate(d.pdc_date))
row = self.prepare_row(party_naming_by, args, gle, outstanding_amount,
credit_note_amount, pdc_amount=pdc_amount, pdc_details=pdc_details)
return row
def allocate_based_on_fifo(self, total_amount, row_amount):
allocated_amount = 0
if row_amount <= total_amount:
allocated_amount = row_amount
total_amount -= row_amount
else:
allocated_amount = total_amount
total_amount = 0
return total_amount, allocated_amount
def prepare_row(self, party_naming_by, args, gle, outstanding_amount, credit_note_amount,
due_date=None, paid_amt=None, payment_term_amount=None, payment_term=None, pdc_amount=None, pdc_details=None):
row = [gle.posting_date, gle.party]
# customer / supplier name
if party_naming_by == "Naming Series":
row += [self.get_party_name(gle.party_type, gle.party)]
# get due date
if not due_date:
due_date = self.voucher_details.get(gle.voucher_no, {}).get("due_date", "")
bill_date = self.voucher_details.get(gle.voucher_no, {}).get("bill_date", "")
row += [gle.voucher_type, gle.voucher_no, due_date]
# get supplier bill details
if args.get("party_type") == "Supplier":
row += [
self.voucher_details.get(gle.voucher_no, {}).get("bill_no", ""),
self.voucher_details.get(gle.voucher_no, {}).get("bill_date", "")
]
# invoiced and paid amounts
invoiced_amount = gle.get(self.dr_or_cr) if (gle.get(self.dr_or_cr) > 0) else 0
if self.filters.based_on_payment_terms:
row+=[payment_term, invoiced_amount]
if payment_term_amount:
invoiced_amount = payment_term_amount
if not payment_term_amount:
paid_amt = invoiced_amount - outstanding_amount - credit_note_amount
row += [invoiced_amount, paid_amt, credit_note_amount, outstanding_amount]
# ageing data
if self.filters.ageing_based_on == "Due Date":
entry_date = due_date
elif self.filters.ageing_based_on == "Supplier Invoice Date":
entry_date = bill_date
else:
entry_date = gle.posting_date
row += get_ageing_data(cint(self.filters.range1), cint(self.filters.range2),
cint(self.filters.range3), self.age_as_on, entry_date, outstanding_amount)
# issue 6371-Ageing buckets should not have amounts if due date is not reached
if self.filters.ageing_based_on == "Due Date" \
and getdate(due_date) > getdate(self.filters.report_date):
row[-1]=row[-2]=row[-3]=row[-4]=0
if self.filters.ageing_based_on == "Supplier Invoice Date" \
and getdate(bill_date) > getdate(self.filters.report_date):
row[-1]=row[-2]=row[-3]=row[-4]=0
if self.filters.get(scrub(args.get("party_type"))):
row.append(gle.account_currency)
else:
row.append(self.company_currency)
remaining_balance = outstanding_amount - flt(pdc_amount)
pdc_details = ", ".join(pdc_details)
row += [pdc_details, pdc_amount, remaining_balance]
if args.get('party_type') == 'Customer':
# customer LPO
row += [self.voucher_details.get(gle.voucher_no, {}).get("po_no")]
# Delivery Note
row += [self.voucher_details.get(gle.voucher_no, {}).get("delivery_note")]
# customer territory / supplier group
if args.get("party_type") == "Customer":
row += [self.get_territory(gle.party), self.get_customer_group(gle.party),
self.voucher_details.get(gle.voucher_no, {}).get("sales_person")]
if args.get("party_type") == "Supplier":
row += [self.get_supplier_group(gle.party)]
row.append(gle.remarks)
return row
def get_entries_after(self, report_date, party_type):
# returns a distinct list
return list(set([(e.voucher_type, e.voucher_no) for e in self.get_gl_entries(party_type, report_date, for_future=True)]))
@ -280,25 +384,25 @@ class ReceivablePayableReport(object):
doctype = "Sales Invoice" if party_type=="Customer" else "Purchase Invoice"
return [d.name for d in frappe.get_all(doctype, filters={"is_return": 1, "docstatus": 1})]
def get_outstanding_amount(self, gle, report_date, dr_or_cr, return_entries, currency_precision):
def get_outstanding_amount(self, gle, report_date, dr_or_cr, return_entries):
payment_amount, credit_note_amount = 0.0, 0.0
reverse_dr_or_cr = "credit" if dr_or_cr=="debit" else "debit"
for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no):
if getdate(e.posting_date) <= report_date and e.name!=gle.name:
amount = flt(e.get(reverse_dr_or_cr), currency_precision) - flt(e.get(dr_or_cr), currency_precision)
amount = flt(e.get(reverse_dr_or_cr), self.currency_precision) - flt(e.get(dr_or_cr), self.currency_precision)
if e.voucher_no not in return_entries:
payment_amount += amount
else:
credit_note_amount += amount
outstanding_amount = (flt((flt(gle.get(dr_or_cr), currency_precision)
- flt(gle.get(reverse_dr_or_cr), currency_precision)
- payment_amount - credit_note_amount), currency_precision))
outstanding_amount = (flt((flt(gle.get(dr_or_cr), self.currency_precision)
- flt(gle.get(reverse_dr_or_cr), self.currency_precision)
- payment_amount - credit_note_amount), self.currency_precision))
credit_note_amount = flt(credit_note_amount, currency_precision)
credit_note_amount = flt(credit_note_amount, self.currency_precision)
return outstanding_amount, credit_note_amount
return outstanding_amount, credit_note_amount, payment_amount
def get_party_name(self, party_type, party_name):
return self.get_party_map(party_type).get(party_name, {}).get("customer_name" if party_type == "Customer" else "supplier_name") or ""
@ -383,7 +487,7 @@ class ReceivablePayableReport(object):
conditions.append("""party in (select name from tabCustomer
where exists(select name from `tabCustomer Group` where lft >= {0} and rgt <= {1}
and name=tabCustomer.customer_group))""".format(lft, rgt))
if self.filters.get("territory"):
lft, rgt = frappe.db.get_value("Territory",
self.filters.get("territory"), ["lft", "rgt"])
@ -415,7 +519,7 @@ class ReceivablePayableReport(object):
conditions.append("""party in (select name from tabSupplier
where supplier_group=%s)""")
values.append(self.filters.get("supplier_group"))
return " and ".join(conditions), values
def get_gl_entries_for(self, party, party_type, against_voucher_type, against_voucher):
@ -432,6 +536,31 @@ class ReceivablePayableReport(object):
.get(against_voucher_type, {})\
.get(against_voucher, [])
def get_payment_term_detail(self, voucher_nos):
payment_term_map = frappe._dict()
payment_terms_details = frappe.db.sql(""" select si.name,
party_account_currency, currency, si.conversion_rate,
ps.due_date, ps.payment_amount, ps.description
from `tabSales Invoice` si, `tabPayment Schedule` ps
where si.name = ps.parent and
si.docstatus = 1 and si.company = '%s' and
si.name in (%s) order by ps.due_date
""" % (frappe.db.escape(self.filters.company), ','.join(['%s'] *len(voucher_nos))),
(tuple(voucher_nos)), as_dict = 1)
for d in payment_terms_details:
if self.filters.get("customer") and d.currency == d.party_account_currency:
payment_term_amount = d.payment_amount
else:
payment_term_amount = flt(flt(d.payment_amount) * flt(d.conversion_rate), self.currency_precision)
payment_term_map.setdefault(d.name, []).append(frappe._dict({
"due_date": d.due_date,
"payment_term_amount": payment_term_amount,
"description": d.description
}))
return payment_term_map
def get_chart_data(self, columns, data):
ageing_columns = columns[self.ageing_col_idx_start : self.ageing_col_idx_start+4]
@ -479,12 +608,11 @@ def get_ageing_data(first_range, second_range, third_range, age_as_on, entry_dat
def get_pdc_details(party_type, report_date):
pdc_details = frappe._dict()
for pdc in frappe.db.sql("""
pdc_via_pe = frappe.db.sql("""
select
pref.reference_name as invoice_no, pent.party, pent.party_type,
max(pent.posting_date) as pdc_date, sum(ifnull(pref.allocated_amount,0)) as pdc_amount,
GROUP_CONCAT(pent.reference_no SEPARATOR ', ') as pdc_ref
pent.posting_date as pdc_date, ifnull(pref.allocated_amount,0) as pdc_amount,
pent.reference_no as pdc_ref
from
`tabPayment Entry` as pent inner join `tabPayment Entry Reference` as pref
on
@ -492,19 +620,22 @@ def get_pdc_details(party_type, report_date):
where
pent.docstatus < 2 and pent.posting_date > %s
and pent.party_type = %s
group by pent.party, pref.reference_name""", (report_date, party_type), as_dict=1):
pdc_details.setdefault((pdc.invoice_no, pdc.party), pdc)
""", (report_date, party_type), as_dict=1)
for pdc in pdc_via_pe:
pdc_details.setdefault((pdc.invoice_no, pdc.party), []).append(pdc)
if scrub(party_type):
amount_field = ("jea.debit_in_account_currency"
if party_type == 'Supplier' else "jea.credit_in_account_currency")
else:
amount_field = "jea.debit + jea.credit"
for pdc in frappe.db.sql("""
pdc_via_je = frappe.db.sql("""
select
jea.reference_name as invoice_no, jea.party, jea.party_type,
max(je.posting_date) as pdc_date, sum(ifnull({0},0)) as pdc_amount,
GROUP_CONCAT(je.cheque_no SEPARATOR ', ') as pdc_ref
je.posting_date as pdc_date, ifnull({0},0) as pdc_amount,
je.cheque_no as pdc_ref
from
`tabJournal Entry` as je inner join `tabJournal Entry Account` as jea
on
@ -512,16 +643,10 @@ def get_pdc_details(party_type, report_date):
where
je.docstatus < 2 and je.posting_date > %s
and jea.party_type = %s
group by jea.party, jea.reference_name""".format(amount_field), (report_date, party_type), as_dict=1):
if (pdc.invoice_no, pdc.party) in pdc_details:
key = (pdc.invoice_no, pdc.party)
pdc_details[key]["pdc_amount"] += pdc.pdc_amount
if pdc.pdc_ref:
pdc_details[key]["pdc_ref"] += ", " + pdc.pdc_ref
if pdc.pdc_date:
pdc_details[key]["pdc_date"] = max(pdc_details[key]["pdc_date"], pdc.pdc_date)
else:
pdc_details.setdefault((pdc.invoice_no, pdc.party), pdc)
""".format(amount_field), (report_date, party_type), as_dict=1)
for pdc in pdc_via_je:
pdc_details.setdefault((pdc.invoice_no, pdc.party), []).append(pdc)
return pdc_details

View File

@ -0,0 +1,84 @@
import frappe
import frappe.defaults
import unittest
from frappe.utils import today, getdate, add_days
from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
class TestAccountsReceivable(unittest.TestCase):
def test_accounts_receivable(self):
frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company 2'")
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 2'")
filters = {
'company': '_Test Company 2',
'based_on_payment_terms': 1
}
name = make_sales_invoice()
report = execute(filters)
expected_data = [[100,30], [100,50], [100,20]]
self.assertEqual(expected_data[0], report[1][0][6:8])
self.assertEqual(expected_data[1], report[1][1][6:8])
self.assertEqual(expected_data[2], report[1][2][6:8])
make_payment(name)
report = execute(filters)
expected_data_after_payment = [[100,50], [100,20]]
self.assertEqual(expected_data_after_payment[0], report[1][0][6:8])
self.assertEqual(expected_data_after_payment[1], report[1][1][6:8])
make_credit_note(name)
report = execute(filters)
expected_data_after_credit_note = [[100,100,30,100,-30]]
self.assertEqual(expected_data_after_credit_note[0], report[1][0][6:11])
def make_sales_invoice():
frappe.set_user("Administrator")
si = create_sales_invoice(company="_Test Company 2",
customer = '_Test Customer 2',
currency = 'EUR',
warehouse = 'Finished Goods - _TC2',
debit_to = 'Debtors - _TC2',
income_account = 'Sales - _TC2',
expense_account = 'Cost of Goods Sold - _TC2',
cost_center = '_Test Company 2 - _TC2',
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(), 60)), invoice_portion=50.00, payment_amount=50))
si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 90)), invoice_portion=20.00, payment_amount=20))
si.submit()
return si.name
def make_payment(docname):
pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=30)
pe.paid_from = "Debtors - _TC2"
pe.insert()
pe.submit()
def make_credit_note(docname):
create_sales_invoice(company="_Test Company 2",
customer = '_Test Customer 2',
currency = 'EUR',
qty = -1,
warehouse = 'Finished Goods - _TC2',
debit_to = 'Debtors - _TC2',
income_account = 'Sales - _TC2',
expense_account = 'Cost of Goods Sold - _TC2',
cost_center = '_Test Company 2 - _TC2',
is_return = 1,
return_against = docname)

View File

@ -213,7 +213,7 @@ def prepare_data(accounts, balance_must_be, period_list, company_currency):
total = 0
row = frappe._dict({
"account": _(d.name),
"parent_account": _(d.parent_account),
"parent_account": _(d.parent_account) if d.parent_account else '',
"indent": flt(d.indent),
"year_start_date": year_start_date,
"year_end_date": year_end_date,

View File

@ -25,7 +25,7 @@ def execute(filters=None):
account_details.setdefault(acc.name, acc)
if filters.get('party'):
parties = str(filters.get("party")).strip()
parties = cstr(filters.get("party")).strip()
filters.party = [d.strip() for d in parties.split(',') if d]
validate_filters(filters, account_details)
@ -60,11 +60,11 @@ def validate_filters(filters, account_details):
frappe.throw(_("From Date must be before To Date"))
if filters.get('project'):
projects = str(filters.get("project")).strip()
projects = cstr(filters.get("project")).strip()
filters.project = [d.strip() for d in projects.split(',') if d]
if filters.get('cost_center'):
cost_centers = str(filters.get("cost_center")).strip()
cost_centers = cstr(filters.get("cost_center")).strip()
filters.cost_center = [d.strip() for d in cost_centers.split(',') if d]

View File

@ -108,33 +108,33 @@ def get_conditions(filters):
def get_pos_invoice_data(filters):
conditions = get_conditions(filters)
result = frappe.db.sql(''
'SELECT '
'posting_date, owner, sum(net_total) as "net_total", sum(total_taxes) as "total_taxes", '
'sum(paid_amount) as "paid_amount", sum(outstanding_amount) as "outstanding_amount", '
'mode_of_payment, warehouse, cost_center '
'FROM ('
'SELECT '
'parent, item_code, sum(amount) as "base_total", warehouse, cost_center '
'from `tabSales Invoice Item` group by parent'
') t1 '
'left join '
'(select parent, mode_of_payment from `tabSales Invoice Payment` group by parent) t3 '
'on (t3.parent = t1.parent) '
'JOIN ('
'SELECT '
'docstatus, company, is_pos, name, posting_date, owner, sum(base_total) as "base_total", '
'sum(net_total) as "net_total", sum(total_taxes_and_charges) as "total_taxes", '
'sum(base_paid_amount) as "paid_amount", sum(outstanding_amount) as "outstanding_amount" '
'FROM `tabSales Invoice` '
'GROUP BY name'
') a '
'ON ('
't1.parent = a.name and t1.base_total = a.base_total) '
'WHERE a.docstatus = 1'
' AND {conditions} '
'GROUP BY '
'owner, posting_date, warehouse'.format(conditions=conditions), filters, as_dict=1
)
'SELECT '
'posting_date, owner, sum(net_total) as "net_total", sum(total_taxes) as "total_taxes", '
'sum(paid_amount) as "paid_amount", sum(outstanding_amount) as "outstanding_amount", '
'mode_of_payment, warehouse, cost_center '
'FROM ('
'SELECT '
'parent, item_code, sum(amount) as "base_total", warehouse, cost_center '
'from `tabSales Invoice Item` group by parent'
') t1 '
'left join '
'(select parent, mode_of_payment from `tabSales Invoice Payment` group by parent) t3 '
'on (t3.parent = t1.parent) '
'JOIN ('
'SELECT '
'docstatus, company, is_pos, name, posting_date, owner, sum(base_total) as "base_total", '
'sum(net_total) as "net_total", sum(total_taxes_and_charges) as "total_taxes", '
'sum(base_paid_amount) as "paid_amount", sum(outstanding_amount) as "outstanding_amount" '
'FROM `tabSales Invoice` '
'GROUP BY name'
') a '
'ON ('
't1.parent = a.name and t1.base_total = a.base_total) '
'WHERE a.docstatus = 1'
' AND {conditions} '
'GROUP BY '
'owner, posting_date, warehouse'.format(conditions=conditions), filters, as_dict=1
)
return result
@ -156,7 +156,6 @@ def get_sales_invoice_data(filters):
def get_mode_of_payments(filters):
frappe.log_error(filters, 'filters')
mode_of_payments = {}
invoice_list = get_invoices(filters)
invoice_list_names = ",".join(['"' + invoice['name'] + '"' for invoice in invoice_list])
@ -164,12 +163,14 @@ def get_mode_of_payments(filters):
inv_mop = frappe.db.sql("""select a.owner,a.posting_date, ifnull(b.mode_of_payment, '') as mode_of_payment
from `tabSales Invoice` a, `tabSales Invoice Payment` b
where a.name = b.parent
and a.docstatus = 1
and a.name in ({invoice_list_names})
union
select a.owner,a.posting_date, ifnull(b.mode_of_payment, '') as mode_of_payment
from `tabSales Invoice` a, `tabPayment Entry` b,`tabPayment Entry Reference` c
where a.name = c.reference_name
and b.name = c.parent
and b.docstatus = 1
and a.name in ({invoice_list_names})
union
select a.owner, a.posting_date,
@ -196,13 +197,13 @@ def get_invoices(filters):
def get_mode_of_payment_details(filters):
mode_of_payment_details = {}
invoice_list = get_invoices(filters)
frappe.log_error(invoice_list, 'invoice_list')
invoice_list_names = ",".join(['"' + invoice['name'] + '"' for invoice in invoice_list])
if invoice_list:
inv_mop_detail = frappe.db.sql("""select a.owner, a.posting_date,
ifnull(b.mode_of_payment, '') as mode_of_payment, sum(b.base_amount) as paid_amount
from `tabSales Invoice` a, `tabSales Invoice Payment` b
where a.name = b.parent
and a.docstatus = 1
and a.name in ({invoice_list_names})
group by a.owner, a.posting_date, mode_of_payment
union
@ -211,6 +212,7 @@ def get_mode_of_payment_details(filters):
from `tabSales Invoice` a, `tabPayment Entry` b,`tabPayment Entry Reference` c
where a.name = c.reference_name
and b.name = c.parent
and b.docstatus = 1
and a.name in ({invoice_list_names})
group by a.owner, a.posting_date, mode_of_payment
union

View File

@ -0,0 +1,165 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import unittest
import frappe
from erpnext.accounts.report.sales_payment_summary.sales_payment_summary import get_mode_of_payments, get_mode_of_payment_details
from frappe.utils import today
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
test_dependencies = ["Sales Invoice"]
class TestSalesPaymentSummary(unittest.TestCase):
@classmethod
def setUpClass(self):
create_records()
pes = frappe.get_all("Payment Entry")
jes = frappe.get_all("Journal Entry")
sis = frappe.get_all("Sales Invoice")
for pe in pes:
frappe.db.set_value("Payment Entry", pe.name, "docstatus", 2)
for je in jes:
frappe.db.set_value("Journal Entry", je.name, "docstatus", 2)
for si in sis:
frappe.db.set_value("Sales Invoice", si.name, "docstatus", 2)
def test_get_mode_of_payments(self):
filters = get_filters()
for dummy in range(2):
si = create_sales_invoice_record()
si.insert()
si.submit()
if int(si.name[-3:])%2 == 0:
bank_account = "_Test Cash - _TC"
mode_of_payment = "Cash"
else:
bank_account = "_Test Bank - _TC"
mode_of_payment = "Credit Card"
pe = get_payment_entry("Sales Invoice", si.name, bank_account=bank_account)
pe.reference_no = "_Test"
pe.reference_date = today()
pe.mode_of_payment = mode_of_payment
pe.insert()
pe.submit()
mop = get_mode_of_payments(filters)
self.assertTrue('Credit Card' in mop.values()[0])
self.assertTrue('Cash' in mop.values()[0])
# Cancel all Cash payment entry and check if this mode of payment is still fetched.
payment_entries = frappe.get_all("Payment Entry", filters={"mode_of_payment": "Cash", "docstatus": 1}, fields=["name", "docstatus"])
for payment_entry in payment_entries:
pe = frappe.get_doc("Payment Entry", payment_entry.name)
pe.cancel()
mop = get_mode_of_payments(filters)
self.assertTrue('Credit Card' in mop.values()[0])
self.assertTrue('Cash' not in mop.values()[0])
def test_get_mode_of_payments_details(self):
filters = get_filters()
for dummy in range(2):
si = create_sales_invoice_record()
si.insert()
si.submit()
if int(si.name[-3:])%2 == 0:
bank_account = "_Test Cash - _TC"
mode_of_payment = "Cash"
else:
bank_account = "_Test Bank - _TC"
mode_of_payment = "Credit Card"
pe = get_payment_entry("Sales Invoice", si.name, bank_account=bank_account)
pe.reference_no = "_Test"
pe.reference_date = today()
pe.mode_of_payment = mode_of_payment
pe.insert()
pe.submit()
mopd = get_mode_of_payment_details(filters)
mopd_values = mopd.values()[0]
for mopd_value in mopd_values:
if mopd_value[0] == "Credit Card":
cc_init_amount = mopd_value[1]
# Cancel one Credit Card Payment Entry and check that it is not fetched in mode of payment details.
payment_entries = frappe.get_all("Payment Entry", filters={"mode_of_payment": "Credit Card", "docstatus": 1}, fields=["name", "docstatus"])
for payment_entry in payment_entries[:1]:
pe = frappe.get_doc("Payment Entry", payment_entry.name)
pe.cancel()
mopd = get_mode_of_payment_details(filters)
mopd_values = mopd.values()[0]
for mopd_value in mopd_values:
if mopd_value[0] == "Credit Card":
cc_final_amount = mopd_value[1]
self.assertTrue(cc_init_amount > cc_final_amount)
def get_filters():
return {
"from_date": "1900-01-01",
"to_date": today(),
"company": "_Test Company"
}
def create_sales_invoice_record(qty=1):
# return sales invoice doc object
return frappe.get_doc({
"doctype": "Sales Invoice",
"customer": frappe.get_doc('Customer', {"customer_name": "Prestiga-Biz"}).name,
"company": '_Test Company',
"due_date": today(),
"posting_date": today(),
"currency": "INR",
"taxes_and_charges": "",
"debit_to": "Debtors - _TC",
"taxes": [],
"items": [{
'doctype': 'Sales Invoice Item',
'item_code': frappe.get_doc('Item', {'item_name': 'Consulting'}).name,
'qty': qty,
"rate": 10000,
'income_account': 'Sales - _TC',
'cost_center': 'Main - _TC',
'expense_account': 'Cost of Goods Sold - _TC'
}]
})
def create_records():
if frappe.db.exists("Customer", "Prestiga-Biz"):
return
#customer
frappe.get_doc({
"customer_group": "_Test Customer Group",
"customer_name": "Prestiga-Biz",
"customer_type": "Company",
"doctype": "Customer",
"territory": "_Test Territory"
}).insert()
# item
item = frappe.get_doc({
"doctype": "Item",
"item_code": "Consulting",
"item_name": "Consulting",
"item_group": "All Item Groups",
"company": "_Test Company",
"is_stock_item": 0
}).insert()
# item price
frappe.get_doc({
"doctype": "Item Price",
"price_list": "Standard Selling",
"item_code": item.item_code,
"price_list_rate": 10000
}).insert()

View File

@ -315,7 +315,6 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order",
source_doctype: "Material Request",
target: me.frm,
args: args,
setters: {
company: me.frm.doc.company
},

View File

@ -91,6 +91,7 @@ class PurchaseOrder(BuyingController):
self.party_account_currency = get_party_account_currency("Supplier", self.supplier, self.company)
def validate_minimum_order_qty(self):
if not self.get("items"): return
items = list(set([d.item_code for d in self.get("items")]))
itemwise_min_order_qty = frappe._dict(frappe.db.sql("""select name, min_order_qty

View File

@ -77,9 +77,20 @@ frappe.query_reports["Purchase Analytics"] = {
events: {
onCheckRow: function(data) {
row_name = data[2].content;
row_values = data.slice(5).map(function (column) {
return column.content;
})
length = data.length;
var tree_type = frappe.query_report.filters[0].value;
if(tree_type == "Supplier" || tree_type == "Item") {
row_values = data.slice(4,length-1).map(function (column) {
return column.content;
})
}
else {
row_values = data.slice(3,length-1).map(function (column) {
return column.content;
})
}
entry = {
'name':row_name,

View File

@ -65,8 +65,8 @@ def get_quantity_list(item):
if item:
qty_list = frappe.db.sql("""select distinct qty from `tabSupplier Quotation Item`
where ifnull(item_code,'')=%s and docstatus < 2""", item, as_dict=1)
qty_list.sort(reverse=False)
where ifnull(item_code,'')=%s and docstatus < 2 order by qty""", item, as_dict=1)
for qt in qty_list:
col = frappe._dict({
"key": str(qt.qty),

View File

@ -561,10 +561,6 @@ def get_data():
"label": _("Subscription Management"),
"icon": "fa fa-microchip ",
"items": [
{
"type": "doctype",
"name": "Subscriber",
},
{
"type": "doctype",
"name": "Subscription Plan",

View File

@ -678,7 +678,7 @@ class BuyingController(StockController):
frappe.db.sql("delete from `tabSerial No` where purchase_document_no=%s", self.name)
def validate_schedule_date(self):
if not self.schedule_date:
if not self.schedule_date and self.get("items"):
self.schedule_date = min([d.schedule_date for d in self.get("items")])
if self.schedule_date:

View File

@ -12,6 +12,9 @@ from erpnext.controllers.accounts_controller import AccountsController
from erpnext.stock.stock_ledger import get_valuation_rate
from erpnext.stock import get_warehouse_account_map
class QualityInspectionRequiredError(frappe.ValidationError): pass
class QualityInspectionRejectedError(frappe.ValidationError): pass
class StockController(AccountsController):
def validate(self):
super(StockController, self).validate()
@ -317,7 +320,6 @@ class StockController(AccountsController):
def validate_inspection(self):
'''Checks if quality inspection is set for Items that require inspection.
On submit, throw an exception'''
inspection_required_fieldname = None
if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
inspection_required_fieldname = "inspection_required_before_purchase"
@ -330,17 +332,25 @@ class StockController(AccountsController):
return
for d in self.get('items'):
raise_exception = False
qa_required = False
if (inspection_required_fieldname and not d.quality_inspection and
frappe.db.get_value("Item", d.item_code, inspection_required_fieldname)):
raise_exception = True
qa_required = True
elif self.doctype == "Stock Entry" and not d.quality_inspection and d.t_warehouse:
raise_exception = True
qa_required = True
if raise_exception:
if qa_required:
frappe.msgprint(_("Quality Inspection required for Item {0}").format(d.item_code))
if self.docstatus==1:
raise frappe.ValidationError
raise QualityInspectionRequiredError
elif self.docstatus == 1:
if d.quality_inspection:
qa_doc = frappe.get_doc("Quality Inspection", d.quality_inspection)
qa_failed = any([r.status=="Rejected" for r in qa_doc.readings])
if qa_failed:
frappe.throw(_("Row {0}: Quality Inspection rejected for item {1}")
.format(d.idx, d.item_code), QualityInspectionRejectedError)
def update_blanket_order(self):
blanket_orders = list(set([d.blanket_order for d in self.items if d.blanket_order]))

View File

@ -376,7 +376,7 @@ def setup_pos_profile():
company_abbr = frappe.get_cached_value('Company', erpnext.get_default_company(), "abbr")
pos = frappe.new_doc('POS Profile')
pos.user = frappe.db.get_global('demo_accounts_user')
pos.pos_profile_name = "Demo POS Profile"
pos.name = "Demo POS Profile"
pos.naming_series = 'SINV-'
pos.update_stock = 0
pos.write_off_account = 'Cost of Goods Sold - '+ company_abbr

View File

@ -1,18 +1,18 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2016-06-22 02:58:41.024538",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"is_standard": "Yes",
"modified": "2017-11-10 19:41:37.320224",
"modified": "2018-12-17 16:46:46.176620",
"modified_by": "Administrator",
"module": "Education",
"name": "Student Fee Collection",
"owner": "Administrator",
"query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(paid_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nGROUP BY\n student",
"prepared_report": 0,
"query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(grand_total) - sum(outstanding_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nGROUP BY\n student",
"ref_doctype": "Fees",
"report_name": "Student Fee Collection",
"report_type": "Query Report",

View File

@ -12,7 +12,7 @@ app_license = "GNU General Public License (v3)"
source_link = "https://github.com/frappe/erpnext"
develop_version = '12.x.x-develop'
staging_version = '11.0.3-beta.30'
staging_version = '11.0.3-beta.33'
error_report_email = "support@erpnext.com"

View File

@ -1,9 +1,24 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
import unittest
test_ignore = ["Leave Block List"]
class TestDepartment(unittest.TestCase):
def test_remove_department_data(self):
doc = create_department("Test Department")
frappe.delete_doc('Department', doc.name)
def create_department(department_name, parent_department=None):
doc = frappe.get_doc({
'doctype': 'Department',
'is_group': 0,
'parent_department': parent_department,
'department_name': department_name,
'company': frappe.defaults.get_defaults().company
}).insert()
return doc
import frappe
test_records = frappe.get_test_records('Department')

View File

@ -13,8 +13,8 @@ from frappe.model.document import Document
from erpnext.utilities.transaction_base import delete_events
from frappe.utils.nestedset import NestedSet
class EmployeeUserDisabledError(frappe.ValidationError):
pass
class EmployeeUserDisabledError(frappe.ValidationError): pass
class EmployeeLeftValidationError(frappe.ValidationError): pass
class Employee(NestedSet):
nsm_parent_field = 'reports_to'
@ -62,8 +62,8 @@ class Employee(NestedSet):
def validate_user_details(self):
data = frappe.db.get_value('User',
self.user_id, ['enabled', 'user_image'], as_dict=1)
self.image = data.get("user_image")
if data.get("user_image"):
self.image = data.get("user_image")
self.validate_for_enabled_user_id(data.get("enabled", 0))
self.validate_duplicate_user_id()
@ -147,8 +147,16 @@ class Employee(NestedSet):
validate_email_add(self.personal_email, True)
def validate_status(self):
if self.status == 'Left' and not self.relieving_date:
throw(_("Please enter relieving date."))
if self.status == 'Left':
reports_to = frappe.db.get_all('Employee',
filters={'reports_to': self.name}
)
if reports_to:
link_to_employees = [frappe.utils.get_link_to_form('Employee', employee.name) for employee in reports_to]
throw(_("Employee status cannot be set to 'Left' as following employees are currently reporting to this employee:&nbsp;")
+ ', '.join(link_to_employees), EmployeeLeftValidationError)
if not self.relieving_date:
throw(_("Please enter relieving date."))
def validate_for_enabled_user_id(self, enabled):
if not self.status == 'Active':

View File

@ -7,6 +7,7 @@ import frappe
import erpnext
import unittest
import frappe.utils
from erpnext.hr.doctype.employee.employee import EmployeeLeftValidationError
test_records = frappe.get_test_records('Employee')
@ -32,6 +33,18 @@ class TestEmployee(unittest.TestCase):
email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
self.assertTrue("Subject: Birthday Reminder" in email_queue[0].message)
def test_employee_status_left(self):
employee1 = make_employee("test_employee_1@company.com")
employee2 = make_employee("test_employee_2@company.com")
employee1_doc = frappe.get_doc("Employee", employee1)
employee2_doc = frappe.get_doc("Employee", employee2)
employee2_doc.reload()
employee2_doc.reports_to = employee1_doc.name
employee2_doc.save()
employee1_doc.reload()
employee1_doc.status = 'Left'
self.assertRaises(EmployeeLeftValidationError, employee1_doc.save)
def make_employee(user):
if not frappe.db.get_value("User", user):
frappe.get_doc({

View File

@ -1,5 +1,5 @@
frappe.listview_settings['Employee Separation'] = {
add_fields: ["boarding_status", "employee_name", "date_of_joining", "department"],
add_fields: ["boarding_status", "employee_name", "department"],
filters:[["boarding_status","=", "Pending"]],
get_indicator: function(doc) {
return [__(doc.boarding_status), frappe.utils.guess_colour(doc.boarding_status), "status,=," + doc.boarding_status];

View File

@ -14,7 +14,7 @@ frappe.ui.form.on("Leave Application", {
doctype: frm.doc.doctype
}
};
});
});
frm.set_query("employee", erpnext.queries.employee);
},
@ -83,7 +83,7 @@ frappe.ui.form.on("Leave Application", {
if (!frm.doc.employee && frappe.defaults.get_user_permissions()) {
const perm = frappe.defaults.get_user_permissions();
if (perm && perm['Employee']) {
frm.set_value('employee', perm['Employee']["docs"][0])
frm.set_value('employee', perm['Employee'].map(perm_doc => perm_doc.doc)[0]);
}
}
},

View File

@ -187,7 +187,7 @@ class LeaveApplication(Document):
self.total_leave_days = get_number_of_leave_days(self.employee, self.leave_type,
self.from_date, self.to_date, self.half_day, self.half_day_date)
if self.total_leave_days == 0:
if self.total_leave_days <= 0:
frappe.throw(_("The day(s) on which you are applying for leave are holidays. You need not apply for leave."))
if not is_lwp(self.leave_type):

View File

@ -457,7 +457,7 @@ def get_leave_period():
return frappe.get_doc(dict(
name = 'Test Leave Period',
doctype = 'Leave Period',
from_date = "{0}-01-01".format(now_datetime().year),
from_date = "{0}-12-01".format(now_datetime().year - 1),
to_date = "{0}-12-31".format(now_datetime().year),
company = "_Test Company",
is_active = 1

View File

@ -124,6 +124,7 @@ frappe.ui.form.on('Salary Structure', {
"label":__("Employee"),
"fieldname":"employee",
"fieldtype":"Select",
"reqd": true,
options: employees
}, {
fieldname:"fetch",

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import flt, cint
from frappe.utils import flt, cint, cstr
from frappe import _
from frappe.model.mapper import get_mapped_doc
from frappe.model.document import Document
@ -22,7 +22,7 @@ class SalaryStructure(Document):
overwritten_fields_if_missing = ["amount_based_on_formula", "formula", "amount"]
for table in ["earnings", "deductions"]:
for d in self.get(table):
component_default_value = frappe.db.get_value("Salary Component", str(d.salary_component),
component_default_value = frappe.db.get_value("Salary Component", cstr(d.salary_component),
overwritten_fields + overwritten_fields_if_missing, as_dict=1)
if component_default_value:
for fieldname in overwritten_fields:

View File

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
from frappe.utils import getdate
from erpnext.hr.doctype.upload_attendance.upload_attendance import get_data
from erpnext.hr.doctype.employee.test_employee import make_employee
class TestUploadAttendance(unittest.TestCase):
def test_date_range(self):
employee = make_employee("test_employee@company.com")
employee_doc = frappe.get_doc("Employee", employee)
date_of_joining = "2018-01-02"
relieving_date = "2018-01-03"
from_date = "2018-01-01"
to_date = "2018-01-04"
employee_doc.date_of_joining = date_of_joining
employee_doc.relieving_date = relieving_date
employee_doc.save()
args = {
"from_date": from_date,
"to_date": to_date
}
data = get_data(args)
filtered_data = []
for row in data:
if row[1] == employee:
filtered_data.append(row)
for row in filtered_data:
self.assertTrue(getdate(row[3]) >= getdate(date_of_joining) and getdate(row[3]) <= getdate(relieving_date))

View File

@ -41,16 +41,28 @@ def add_header(w):
return w
def add_data(w, args):
data = get_data(args)
writedata(w, data)
return w
def get_data(args):
dates = get_dates(args)
employees = get_active_employees()
existing_attendance_records = get_existing_attendance_records(args)
data = []
for date in dates:
for employee in employees:
if getdate(date) < getdate(employee.date_of_joining):
continue
if employee.relieving_date:
if getdate(date) > getdate(employee.relieving_date):
continue
existing_attendance = {}
if existing_attendance_records \
and tuple([getdate(date), employee.name]) in existing_attendance_records:
and tuple([getdate(date), employee.name]) in existing_attendance_records \
and getdate(employee.date_of_joining) >= getdate(date) \
and getdate(employee.relieving_date) <= getdate(date):
existing_attendance = existing_attendance_records[tuple([getdate(date), employee.name])]
row = [
existing_attendance and existing_attendance.name or "",
employee.name, employee.employee_name, date,
@ -58,8 +70,12 @@ def add_data(w, args):
existing_attendance and existing_attendance.leave_type or "", employee.company,
existing_attendance and existing_attendance.naming_series or get_naming_series(),
]
w.writerow(row)
return w
data.append(row)
return data
def writedata(w, data):
for row in data:
w.writerow(row)
def get_dates(args):
"""get list of dates in between from date and to date"""
@ -68,8 +84,13 @@ def get_dates(args):
return dates
def get_active_employees():
employees = frappe.db.sql("""select name, employee_name, company
from tabEmployee where docstatus < 2 and status = 'Active'""", as_dict=1)
employees = frappe.db.get_all('Employee',
fields=['name', 'employee_name', 'date_of_joining', 'company', 'relieving_date'],
filters={
'docstatus': ['<', 2],
'status': 'Active'
}
)
return employees
def get_existing_attendance_records(args):

View File

@ -402,6 +402,8 @@ frappe.ui.form.on("BOM Item", "items_remove", function(frm) {
var toggle_operations = function(frm) {
frm.toggle_display("operations_section", cint(frm.doc.with_operations) == 1);
frm.toggle_display("transfer_material_against", cint(frm.doc.with_operations) == 1);
frm.toggle_reqd("transfer_material_against", cint(frm.doc.with_operations) == 1);
};
frappe.ui.form.on("BOM", "with_operations", function(frm) {

View File

@ -80,41 +80,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"description": "Quantity of item obtained after manufacturing / repacking from given quantities of raw materials",
"fieldname": "quantity",
"fieldtype": "Float",
"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": "Quantity",
"length": 0,
"no_copy": 0,
"oldfieldname": "quantity",
"oldfieldtype": "Currency",
"permlevel": 0,
"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_in_quick_entry": 0,
@ -154,8 +119,10 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "inspection_required",
"fieldtype": "Check",
"default": "1",
"description": "Quantity of item obtained after manufacturing / repacking from given quantities of raw materials",
"fieldname": "quantity",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -163,51 +130,18 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Inspection Required",
"label": "Quantity",
"length": 0,
"no_copy": 0,
"oldfieldname": "quantity",
"oldfieldtype": "Currency",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "inspection_required",
"fieldname": "quality_inspection_template",
"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": "Quality Inspection Template",
"length": 0,
"no_copy": 0,
"options": "Quality Inspection Template",
"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,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
@ -346,77 +280,11 @@
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 1,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rm_cost_as_per",
"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": "Rate Of Materials Based On",
"length": 0,
"no_copy": 0,
"options": "Valuation Rate\nLast Purchase Rate\nPrice List",
"permlevel": 0,
"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_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.rm_cost_as_per===\"Price List\"",
"fieldname": "buying_price_list",
"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": "Price List",
"length": 0,
"no_copy": 0,
"options": "Price List",
"permlevel": 0,
"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_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"fieldname": "set_rate_of_sub_assembly_item_based_on_bom",
"fieldname": "inspection_required",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
@ -425,7 +293,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Set rate of sub-assembly item based on BOM",
"label": "Inspection Required",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -481,7 +349,7 @@
"collapsible": 0,
"columns": 0,
"fieldname": "allow_same_item_multiple_times",
"fieldtype": "Data",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -508,12 +376,12 @@
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "with_operations",
"fieldname": "transfer_material_against_job_card",
"default": "1",
"fieldname": "set_rate_of_sub_assembly_item_based_on_bom",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
@ -522,7 +390,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Transfer Material Against Job Card",
"label": "Set rate of sub-assembly item based on BOM",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -538,6 +406,40 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "inspection_required",
"fieldname": "quality_inspection_template",
"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": "Quality Inspection Template",
"length": 0,
"no_copy": 0,
"options": "Quality Inspection Template",
"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_in_quick_entry": 0,
@ -603,6 +505,72 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "transfer_material_against",
"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": "Transfer Material Against",
"length": 0,
"no_copy": 0,
"options": "\nWork Order\nJob Card",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "conversion_rate",
"fieldtype": "Float",
"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": "Conversion Rate",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "9",
"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_in_quick_entry": 0,
@ -670,12 +638,12 @@
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "conversion_rate",
"fieldtype": "Float",
"fieldname": "rm_cost_as_per",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -683,17 +651,50 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Conversion Rate",
"label": "Rate Of Materials Based On",
"length": 0,
"no_copy": 0,
"options": "Valuation Rate\nLast Purchase Rate\nPrice List",
"permlevel": 0,
"precision": "9",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.rm_cost_as_per===\"Price List\"",
"fieldname": "buying_price_list",
"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": "Price List",
"length": 0,
"no_copy": 0,
"options": "Price List",
"permlevel": 0,
"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,
@ -707,7 +708,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"description": "Specify the operations, operating cost and give a unique Operation no to your operations.",
"description": "",
"fieldname": "operations_section",
"fieldtype": "Section Break",
"hidden": 0,
@ -1976,7 +1977,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-10-24 02:07:21.618275",
"modified": "2018-12-13 17:45:44.843197",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM",

View File

@ -81,7 +81,7 @@ class BOM(WebsiteGenerator):
def get_item_det(self, item_code):
item = frappe.db.sql("""select name, item_name, docstatus, description, image,
is_sub_contracted_item, stock_uom, default_bom, last_purchase_rate, allow_transfer_for_manufacture
is_sub_contracted_item, stock_uom, default_bom, last_purchase_rate, include_item_in_manufacturing
from `tabItem` where name=%s""", item_code, as_dict = 1)
if not item:
@ -109,7 +109,7 @@ class BOM(WebsiteGenerator):
"item_name": item.item_name,
"bom_no": item.bom_no,
"stock_qty": item.stock_qty,
"allow_transfer_for_manufacture": item.allow_transfer_for_manufacture
"include_item_in_manufacturing": item.include_item_in_manufacturing
})
for r in ret:
if not item.get(r):
@ -128,8 +128,8 @@ class BOM(WebsiteGenerator):
self.validate_rm_item(item)
args['bom_no'] = args['bom_no'] or item and cstr(item[0]['default_bom']) or ''
args['transfer_for_manufacture'] = (cstr(args.get('allow_transfer_for_manufacture', '')) or
item and item[0].allow_transfer_for_manufacture or 0)
args['transfer_for_manufacture'] = (cstr(args.get('include_item_in_manufacturing', '')) or
item and item[0].include_item_in_manufacturing or 0)
args.update(item[0])
rate = self.get_rm_rate(args)
@ -145,7 +145,7 @@ class BOM(WebsiteGenerator):
'qty' : args.get("qty") or args.get("stock_qty") or 1,
'stock_qty' : args.get("qty") or args.get("stock_qty") or 1,
'base_rate' : rate,
'allow_transfer_for_manufacture': cint(args['transfer_for_manufacture']) or 0
'include_item_in_manufacturing': cint(args['transfer_for_manufacture']) or 0
}
return ret_item
@ -477,7 +477,7 @@ class BOM(WebsiteGenerator):
'stock_uom' : d.stock_uom,
'stock_qty' : flt(d.stock_qty),
'rate' : d.base_rate,
'allow_transfer_for_manufacture': d.allow_transfer_for_manufacture
'include_item_in_manufacturing': d.include_item_in_manufacturing
}))
def company_currency(self):
@ -494,7 +494,7 @@ class BOM(WebsiteGenerator):
# Did not use qty_consumed_per_unit in the query, as it leads to rounding loss
child_fb_items = frappe.db.sql("""select bom_item.item_code, bom_item.item_name,
bom_item.description, bom_item.source_warehouse, bom_item.operation,
bom_item.stock_uom, bom_item.stock_qty, bom_item.rate, bom_item.allow_transfer_for_manufacture,
bom_item.stock_uom, bom_item.stock_qty, bom_item.rate, bom_item.include_item_in_manufacturing,
bom_item.stock_qty / ifnull(bom.quantity, 1) as qty_consumed_per_unit
from `tabBOM Explosion Item` bom_item, tabBOM bom
where bom_item.parent = bom.name and bom.name = %s and bom.docstatus = 1""", bom_no, as_dict = 1)
@ -509,7 +509,7 @@ class BOM(WebsiteGenerator):
'stock_uom' : d['stock_uom'],
'stock_qty' : d['qty_consumed_per_unit'] * stock_qty,
'rate' : flt(d['rate']),
'allow_transfer_for_manufacture': d.get('allow_transfer_for_manufacture', 0)
'include_item_in_manufacturing': d.get('include_item_in_manufacturing', 0)
}))
def add_exploded_items(self):
@ -585,7 +585,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
query = query.format(table="BOM Explosion Item",
where_conditions="",
is_stock_item=is_stock_item,
select_columns = """, bom_item.source_warehouse, bom_item.operation, bom_item.allow_transfer_for_manufacture,
select_columns = """, bom_item.source_warehouse, bom_item.operation, bom_item.include_item_in_manufacturing,
(Select idx from `tabBOM Item` where item_code = bom_item.item_code and parent = %(parent)s ) as idx""")
items = frappe.db.sql(query, { "parent": bom, "qty": qty, "bom": bom, "company": company }, as_dict=True)
@ -594,7 +594,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
items = frappe.db.sql(query, { "qty": qty, "bom": bom, "company": company }, as_dict=True)
else:
query = query.format(table="BOM Item", where_conditions="", is_stock_item=is_stock_item,
select_columns = ", bom_item.source_warehouse, bom_item.idx, bom_item.operation, bom_item.allow_transfer_for_manufacture")
select_columns = ", bom_item.source_warehouse, bom_item.idx, bom_item.operation, bom_item.include_item_in_manufacturing")
items = frappe.db.sql(query, { "qty": qty, "bom": bom, "company": company }, as_dict=True)
for item in items:

View File

@ -11,7 +11,7 @@
"uom": "_Test UOM",
"stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC",
"allow_transfer_for_manufacture": 1
"include_item_in_manufacturing": 1
},
{
"amount": 2000.0,
@ -23,7 +23,7 @@
"uom": "_Test UOM",
"stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC",
"allow_transfer_for_manufacture": 1
"include_item_in_manufacturing": 1
}
],
"docstatus": 1,
@ -57,7 +57,7 @@
"uom": "_Test UOM",
"stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC",
"allow_transfer_for_manufacture": 1
"include_item_in_manufacturing": 1
},
{
"amount": 2000.0,
@ -69,7 +69,7 @@
"uom": "_Test UOM",
"stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC",
"allow_transfer_for_manufacture": 1
"include_item_in_manufacturing": 1
}
],
"docstatus": 1,
@ -102,7 +102,7 @@
"uom": "_Test UOM",
"stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC",
"allow_transfer_for_manufacture": 1
"include_item_in_manufacturing": 1
},
{
"amount": 3000.0,
@ -115,7 +115,7 @@
"uom": "_Test UOM",
"stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC",
"allow_transfer_for_manufacture": 1
"include_item_in_manufacturing": 1
}
],
"docstatus": 1,
@ -150,7 +150,7 @@
"uom": "_Test UOM",
"stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC",
"allow_transfer_for_manufacture": 1
"include_item_in_manufacturing": 1
}
],
"docstatus": 1,

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@ -573,7 +574,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "allow_transfer_for_manufacture",
"fieldname": "include_item_in_manufacturing",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
@ -582,7 +583,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Transfer for Manufacture",
"label": "Include Item In Manufacturing",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -609,7 +610,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-08-27 16:32:35.152139",
"modified": "2018-11-20 19:04:59.813773",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Explosion Item",

View File

@ -78,6 +78,39 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "operation",
"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": "Item operation",
"length": 0,
"no_copy": 0,
"options": "Operation",
"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_in_quick_entry": 0,
@ -932,8 +965,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "item_code.allow_transfer_for_manufacture",
"fieldname": "allow_transfer_for_manufacture",
"fetch_from": "item_code.include_item_in_manufacturing",
"fieldname": "include_item_in_manufacturing",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
@ -942,7 +975,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Transfer for Manufacture",
"label": "Include Item In Manufacturing",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -990,39 +1023,6 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "operation",
"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": "Item operation",
"length": 0,
"no_copy": 0,
"options": "Operation",
"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
}
],
"has_web_view": 0,
@ -1035,7 +1035,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-11-22 15:04:55.187136",
"modified": "2018-12-28 16:38:56.529079",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Item",

View File

@ -3,7 +3,7 @@
frappe.ui.form.on('Job Card', {
refresh: function(frm) {
if (frm.doc.items && frm.doc.docstatus==1) {
if(!frm.doc.__islocal && frm.doc.items && frm.doc.items.length) {
if (frm.doc.for_quantity != frm.doc.transferred_qty) {
frm.add_custom_button(__("Material Request"), () => {
frm.trigger("make_material_request");
@ -31,6 +31,7 @@ frappe.ui.form.on('Job Card', {
frm.add_custom_button(__("Complete Job"), () => {
frm.set_value('actual_end_date', frappe.datetime.now_datetime());
frm.save();
frm.savesubmit();
});
}
}

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@ -46,6 +47,39 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bom_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": "BOM No",
"length": 0,
"no_copy": 0,
"options": "BOM",
"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_in_quick_entry": 0,
@ -112,39 +146,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "wip_warehouse",
"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": "WIP Warehouse",
"length": 0,
"no_copy": 0,
"options": "Warehouse",
"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_in_quick_entry": 0,
@ -281,9 +282,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "transferred_qty",
"fieldtype": "Float",
"fieldname": "wip_warehouse",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -291,17 +291,18 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Transferred Qty",
"label": "WIP Warehouse",
"length": 0,
"no_copy": 0,
"options": "Warehouse",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
@ -635,8 +636,9 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bom_no",
"fieldtype": "Link",
"default": "0",
"fieldname": "transferred_qty",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -644,10 +646,42 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "BOM No",
"label": "Transferred Qty",
"length": 0,
"no_copy": 0,
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "requested_qty",
"fieldtype": "Float",
"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": "Requested Qty",
"length": 0,
"no_copy": 0,
"options": "BOM",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -701,8 +735,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_20",
"fieldtype": "Column Break",
"fieldname": "remarks",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -710,6 +744,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Remarks",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -732,8 +767,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "remarks",
"fieldtype": "Small Text",
"fieldname": "column_break_20",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -741,7 +776,6 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Remarks",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -776,13 +810,13 @@
"in_standard_filter": 0,
"label": "Status",
"length": 0,
"no_copy": 0,
"options": "Open\nWork In Progress\nCancelled\nCompleted",
"no_copy": 1,
"options": "Open\nWork In Progress\nMaterial Transferred\nSubmitted\nCancelled\nCompleted",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
@ -834,7 +868,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-28 16:50:43.576151",
"modified": "2018-12-13 17:23:57.986381",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Job Card",

View File

@ -11,9 +11,9 @@ from frappe.model.document import Document
class JobCard(Document):
def validate(self):
self.status = 'Open'
self.validate_actual_dates()
self.set_time_in_mins()
self.set_status()
def validate_actual_dates(self):
if get_datetime(self.actual_start_date) > get_datetime(self.actual_end_date):
@ -48,7 +48,7 @@ class JobCard(Document):
return
doc = frappe.get_doc('Work Order', self.get('work_order'))
if not doc.transfer_material_against_job_card and doc.skip_transfer:
if doc.transfer_material_against == 'Work Order' and doc.skip_transfer:
return
for d in doc.required_items:
@ -104,20 +104,23 @@ class JobCard(Document):
wo.set_actual_dates()
wo.save()
def set_transferred_qty(self):
def set_transferred_qty(self, update_status=False):
if not self.items:
self.transferred_qty = self.for_quantity if self.docstatus == 1 else 0
if self.items:
self.transferred_qty = frappe.db.get_value('Stock Entry', {'job_card': self.name,
'work_order': self.work_order, 'docstatus': 1}, 'sum(fg_completed_qty)') or 0
self.transferred_qty = frappe.db.get_value('Stock Entry', {
'job_card': self.name,
'work_order': self.work_order,
'docstatus': 1
}, 'sum(fg_completed_qty)') or 0
self.db_set("transferred_qty", self.transferred_qty)
qty = 0
if self.work_order:
doc = frappe.get_doc('Work Order', self.work_order)
if doc.transfer_material_against_job_card and not doc.skip_transfer:
if doc.transfer_material_against == 'Job Card' and not doc.skip_transfer:
completed = True
for d in doc.operations:
if d.status != 'Completed':
@ -131,15 +134,28 @@ class JobCard(Document):
doc.db_set('material_transferred_for_manufacturing', qty)
self.set_status()
self.set_status(update_status)
def set_status(self):
status = 'Cancelled' if self.docstatus == 2 else 'Work In Progress'
def set_status(self, update_status=False):
self.status = {
0: "Open",
1: "Submitted",
2: "Cancelled"
}[self.docstatus or 0]
if self.for_quantity == self.transferred_qty:
status = 'Completed'
if self.actual_start_date:
self.status = 'Work In Progress'
self.db_set('status', status)
if (self.docstatus == 1 and
(self.for_quantity == self.transferred_qty or not self.items)):
self.status = 'Completed'
if self.status != 'Completed':
if self.for_quantity == self.transferred_qty:
self.status = 'Material Transferred'
if update_status:
self.db_set('status', self.status)
@frappe.whitelist()
def make_material_request(source_name, target_doc=None):

View File

@ -6,6 +6,8 @@ frappe.listview_settings['Job Card'] = {
return [__("Completed"), "green", "status,=,Completed"];
} else if (doc.docstatus == 2) {
return [__("Cancelled"), "red", "status,=,Cancelled"];
} else if (doc.status === "Material Transferred") {
return [__('Material Transferred'), "blue", "status,=,Material Transferred"];
} else {
return [__("Open"), "red", "status,=,Open"];
}

View File

@ -515,7 +515,7 @@ def get_items_for_material_requests(doc, company=None):
doc = frappe._dict(json.loads(doc))
doc['mr_items'] = []
po_items = doc['po_items'] if doc.get('po_items') else doc['items']
po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
for data in po_items:
warehouse = None
@ -534,10 +534,10 @@ def get_items_for_material_requests(doc, company=None):
else:
planned_qty = data.get('planned_qty')
bom_no = data.get('bom_no')
include_subcontracted_items = doc['include_subcontracted_items']
company = doc['company']
include_non_stock_items = doc['include_non_stock_items']
ignore_existing_ordered_qty = doc['ignore_existing_ordered_qty']
include_subcontracted_items = doc.get('include_subcontracted_items')
company = doc.get('company')
include_non_stock_items = doc.get('include_non_stock_items')
ignore_existing_ordered_qty = doc.get('ignore_existing_ordered_qty')
if not planned_qty:
frappe.throw(_("For row {0}: Enter Planned Qty").format(data.get('idx')))

View File

@ -306,7 +306,7 @@ class TestWorkOrder(unittest.TestCase):
items = {'Finished Good Transfer Item': 1, '_Test FG Item': 1, '_Test FG Item 1': 0}
for item, allow_transfer in items.items():
make_item(item, {
'allow_transfer_for_manufacture': allow_transfer
'include_item_in_manufacturing': allow_transfer
})
fg_item = 'Finished Good Transfer Item'

View File

@ -112,11 +112,20 @@ frappe.ui.form.on("Work Order", {
frm.trigger('show_progress');
}
if (frm.doc.docstatus === 1 && frm.doc.operations
if (frm.doc.docstatus === 1
&& frm.doc.operations && frm.doc.operations.length
&& frm.doc.qty != frm.doc.material_transferred_for_manufacturing) {
frm.add_custom_button(__('Make Job Card'), () => {
frm.trigger("make_job_card")
}).addClass('btn-primary');
const not_completed = frm.doc.operations.filter(d => {
if(d.status != 'Completed') {
return true;
}
});
if(not_completed && not_completed.length) {
frm.add_custom_button(__('Make Job Card'), () => {
frm.trigger("make_job_card")
}).addClass('btn-primary');
}
}
if(frm.doc.required_items && frm.doc.allow_alternative_item) {
@ -294,7 +303,7 @@ frappe.ui.form.on("Work Order", {
frm.trigger('set_sales_order');
erpnext.in_production_item_onchange = true;
$.each(["description", "stock_uom", "project", "bom_no",
"allow_alternative_item", "transfer_material_against_job_card"], function(i, field) {
"allow_alternative_item", "transfer_material_against"], function(i, field) {
frm.set_value(field, r.message[field]);
});
@ -340,9 +349,8 @@ frappe.ui.form.on("Work Order", {
before_submit: function(frm) {
frm.toggle_reqd(["fg_warehouse", "wip_warehouse"], true);
frm.fields_dict.required_items.grid.toggle_reqd("source_warehouse", true);
if (frm.doc.operations) {
frm.fields_dict.operations.grid.toggle_reqd("workstation", true);
}
frm.toggle_reqd("transfer_material_against", frm.doc.operations);
frm.fields_dict.operations.grid.toggle_reqd("workstation", frm.doc.operations);
},
set_sales_order: function(frm) {
@ -425,7 +433,7 @@ erpnext.work_order = {
}
const show_start_btn = (frm.doc.skip_transfer
|| frm.doc.transfer_material_against_job_card) ? 0 : 1;
|| frm.doc.transfer_material_against == 'Job Card') ? 0 : 1;
if (show_start_btn){
if ((flt(doc.material_transferred_for_manufacturing) < flt(doc.qty))

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
@ -183,6 +184,38 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "allow_alternative_item",
"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": "Allow Alternative Item",
"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_in_quick_entry": 0,
@ -223,7 +256,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "allow_alternative_item",
"description": "Check if material transfer entry is not required",
"fieldname": "skip_transfer",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
@ -232,7 +266,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Alternative Item",
"label": "Skip Material Transfer",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -486,39 +520,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Check if material transfer entry is not required",
"fieldname": "skip_transfer",
"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": "Skip Material Transfer",
"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_in_quick_entry": 0,
@ -552,39 +553,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "operations",
"fieldname": "transfer_material_against_job_card",
"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": "Transfer Material Against Job Card",
"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_in_quick_entry": 0,
@ -1070,6 +1038,41 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Work Order",
"depends_on": "operations",
"fieldname": "transfer_material_against",
"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": "Transfer Material Against",
"length": 0,
"no_copy": 0,
"options": "\nWork Order\nJob Card",
"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_in_quick_entry": 0,
@ -1672,7 +1675,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-09-05 06:28:22.983369",
"modified": "2018-12-13 15:33:12.490710",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order",

View File

@ -191,7 +191,7 @@ class WorkOrder(Document):
for purpose, fieldname in (("Manufacture", "produced_qty"),
("Material Transfer for Manufacture", "material_transferred_for_manufacturing")):
if (purpose == 'Material Transfer for Manufacture' and
self.operations and self.transfer_material_against_job_card):
self.operations and self.transfer_material_against == 'Job Card'):
continue
qty = flt(frappe.db.sql("""select sum(fg_completed_qty)
@ -459,7 +459,7 @@ class WorkOrder(Document):
'allow_alternative_item': item.allow_alternative_item,
'required_qty': item.qty,
'source_warehouse': item.source_warehouse or item.default_warehouse,
'allow_transfer_for_manufacture': item.allow_transfer_for_manufacture
'include_item_in_manufacturing': item.include_item_in_manufacturing
})
self.set_available_qty()
@ -564,11 +564,11 @@ def get_item_details(item, project = None):
frappe.throw(_("Default BOM for {0} not found").format(item))
bom_data = frappe.db.get_value('BOM', res['bom_no'],
['project', 'allow_alternative_item', 'transfer_material_against_job_card'], as_dict=1)
['project', 'allow_alternative_item', 'transfer_material_against'], as_dict=1)
res['project'] = project or bom_data.project
res['allow_alternative_item'] = bom_data.allow_alternative_item
res['transfer_material_against_job_card'] = bom_data.transfer_material_against_job_card
res['transfer_material_against'] = bom_data.transfer_material_against
res.update(check_if_scrap_warehouse_mandatory(res["bom_no"]))
return res
@ -682,7 +682,7 @@ def create_job_card(work_order, row, qty=0, auto_create=False):
'wip_warehouse': work_order.wip_warehouse
})
if work_order.transfer_material_against_job_card and not work_order.skip_transfer:
if work_order.transfer_material_against == 'Job Card' and not work_order.skip_transfer:
doc.get_required_items()
if auto_create:

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@ -342,7 +343,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "allow_transfer_for_manufacture",
"fieldname": "include_item_in_manufacturing",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
@ -351,7 +352,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Transfer for Manufacture",
"label": "Include Item In Manufacturing",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -506,7 +507,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-10-04 16:16:54.237829",
"modified": "2018-11-20 19:04:38.508839",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order Item",

View File

@ -495,7 +495,7 @@ erpnext.patches.v10_0.set_b2c_limit
erpnext.patches.v10_0.update_translatable_fields
erpnext.patches.v10_0.rename_offer_letter_to_job_offer
execute:frappe.delete_doc('DocType', 'Production Planning Tool', ignore_missing=True)
erpnext.patches.v10_0.migrate_daily_work_summary_settings_to_daily_work_summary_group
erpnext.patches.v10_0.migrate_daily_work_summary_settings_to_daily_work_summary_group # 24-12-2018
erpnext.patches.v10_0.add_default_cash_flow_mappers
erpnext.patches.v11_0.make_quality_inspection_template
erpnext.patches.v10_0.update_status_for_multiple_source_in_po
@ -579,3 +579,4 @@ erpnext.patches.v10_0.update_user_image_in_employee
erpnext.patches.v11_0.update_delivery_trip_status
erpnext.patches.v10_0.repost_gle_for_purchase_receipts_with_rejected_items
erpnext.patches.v11_0.set_missing_gst_hsn_code
erpnext.patches.v11_0.rename_bom_wo_fields

View File

@ -6,37 +6,36 @@ import frappe
def execute():
frappe.reload_doc("hr", "doctype", "daily_work_summary_group")
frappe.reload_doc("hr", "doctype", "daily_work_summary_group_user")
if not frappe.db.table_exists('Daily Work Summary Group'):
frappe.reload_doc("hr", "doctype", "daily_work_summary_group")
frappe.reload_doc("hr", "doctype", "daily_work_summary_group_user")
# check if Daily Work Summary Settings Company table exists
try:
frappe.db.sql('DESC `tabDaily Work Summary Settings Company`')
except Exception:
return
# check if Daily Work Summary Settings Company table exists
try:
frappe.db.sql('DESC `tabDaily Work Summary Settings Company`')
except Exception:
return
# get the previously saved settings
previous_setting = get_previous_setting()
if previous_setting["companies"]:
for d in previous_setting["companies"]:
users = frappe.get_list("Employee", dict(
company=d.company, user_id=("!=", " ")), "user_id as user")
if(len(users)):
# create new group entry for each company entry
new_group = frappe.get_doc(dict(doctype="Daily Work Summary Group",
name="Daily Work Summary for " + d.company,
users=users,
send_emails_at=d.send_emails_at,
subject=previous_setting["subject"],
message=previous_setting["message"]))
new_group.flags.ignore_permissions = True
new_group.flags.ignore_validate = True
new_group.insert(ignore_if_duplicate = True)
frappe.delete_doc("Daily Work Summary Settings")
frappe.delete_doc("Daily Work Summary Settings Company")
# get the previously saved settings
previous_setting = get_previous_setting()
if previous_setting["companies"]:
for d in previous_setting["companies"]:
users = frappe.get_list("Employee", dict(
company=d.company, user_id=("!=", " ")), "user_id as user")
if(len(users)):
# create new group entry for each company entry
new_group = frappe.get_doc(dict(doctype="Daily Work Summary Group",
name="Daily Work Summary for " + d.company,
users=users,
send_emails_at=d.send_emails_at,
subject=previous_setting["subject"],
message=previous_setting["message"]))
new_group.flags.ignore_permissions = True
new_group.flags.ignore_validate = True
new_group.insert(ignore_if_duplicate = True)
def get_setting_companies():
return frappe.db.sql("select * from `tabDaily Work Summary Settings Company`", as_dict=True)
frappe.delete_doc("DocType", "Daily Work Summary Settings")
frappe.delete_doc("DocType", "Daily Work Summary Settings Company")
def get_previous_setting():
@ -47,3 +46,6 @@ def get_previous_setting():
obj[field] = value
obj["companies"] = get_setting_companies()
return obj
def get_setting_companies():
return frappe.db.sql("select * from `tabDaily Work Summary Settings Company`", as_dict=True)

View File

@ -0,0 +1,36 @@
# Copyright (c) 2018, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.utils.rename_field import rename_field
def execute():
for doctype in ['BOM Explosion Item', 'BOM Item', 'Work Order Item', 'Item']:
if frappe.db.has_column(doctype, 'allow_transfer_for_manufacture'):
if doctype != 'Item':
frappe.reload_doc('manufacturing', 'doctype', frappe.scrub(doctype))
else:
frappe.reload_doc('stock', 'doctype', frappe.scrub(doctype))
rename_field(doctype, "allow_transfer_for_manufacture", "include_item_in_manufacturing")
if frappe.db.has_column('BOM', 'allow_same_item_multiple_times'):
frappe.db.sql(""" UPDATE tabBOM
SET
allow_same_item_multiple_times = 0
WHERE
trim(coalesce(allow_same_item_multiple_times, '')) = '' """)
for doctype in ['BOM', 'Work Order']:
frappe.reload_doc('manufacturing', 'doctype', frappe.scrub(doctype))
if frappe.db.has_column(doctype, 'transfer_material_against_job_card'):
frappe.db.sql(""" UPDATE `tab%s`
SET transfer_material_against = CASE WHEN
transfer_material_against_job_card = 1 then 'Job Card' Else 'Work Order' END
WHERE docstatus < 2""" % (doctype))
else:
frappe.db.sql(""" UPDATE `tab%s`
SET transfer_material_against = 'Work Order'
WHERE docstatus < 2""" % (doctype))

View File

@ -1,28 +1,60 @@
import frappe
from frappe.desk.form.linked_with import get_linked_doctypes
# Skips user permission check for doctypes where department link field was recently added
# https://github.com/frappe/erpnext/pull/14121
def execute():
user_permissions = frappe.get_all("User Permission",
filters=[['allow', '=', 'Department']],
fields=['name', 'skip_for_doctype'])
doctypes_to_skip = []
for doctype in ['Appraisal', 'Leave Allocation', 'Expense Claim', 'Instructor', 'Salary Slip',
'Attendance', 'Training Feedback', 'Training Result Employee',
'Leave Application', 'Employee Advance', 'Activity Cost', 'Training Event Employee',
'Timesheet', 'Sales Person', 'Payroll Employee Detail']:
if frappe.db.exists('Custom Field', { 'dt': doctype, 'fieldname': 'department'}): continue
doctypes_to_skip.append(doctype)
doctypes_to_skip = []
frappe.reload_doctype('User Permission')
for doctype in ['Appraisal', 'Leave Allocation', 'Expense Claim', 'Instructor', 'Salary Slip',
'Attendance', 'Training Feedback', 'Training Result Employee',
'Leave Application', 'Employee Advance', 'Activity Cost', 'Training Event Employee',
'Timesheet', 'Sales Person', 'Payroll Employee Detail']:
if frappe.db.exists('Custom Field', { 'dt': doctype, 'fieldname': 'department'}): continue
doctypes_to_skip.append(doctype)
user_permissions = frappe.get_all("User Permission",
filters=[['allow', '=', 'Department'], ['applicable_for', 'in', [None] + doctypes_to_skip]],
fields=['name', 'applicable_for'])
for perm in user_permissions:
skip_for_doctype = perm.get('skip_for_doctype')
user_permissions_to_delete = []
new_user_permissions_list = []
skip_for_doctype = skip_for_doctype.split('\n') + doctypes_to_skip
skip_for_doctype = set(skip_for_doctype) # to remove duplicates
skip_for_doctype = '\n'.join(skip_for_doctype) # convert back to string
for user_permission in user_permissions:
if user_permission.applicable_for:
# simply delete user permission record since it needs to be skipped.
user_permissions_to_delete.append(user_permission.name)
else:
# if applicable_for is `None` it means that user permission is applicable for every doctype
# to avoid this we need to create other user permission records and only skip the listed doctypes in this patch
linked_doctypes = get_linked_doctypes(user_permission.allow, True).keys()
applicable_for_doctypes = list(set(linked_doctypes) - set(doctypes_to_skip))
frappe.set_value('User Permission', perm.name, 'skip_for_doctype', skip_for_doctype)
user_permissions_to_delete.append(user_permission.name)
for doctype in applicable_for_doctypes:
if doctype:
# Maintain sequence (name, user, allow, for_value, applicable_for, apply_to_all_doctypes)
new_user_permissions_list.append((
frappe.generate_hash("", 10),
user_permission.user,
user_permission.allow,
user_permission.for_value,
doctype,
0
))
if new_user_permissions_list:
frappe.db.sql('''
INSERT INTO `tabUser Permission`
(`name`, `user`, `allow`, `for_value`, `applicable_for`, `apply_to_all_doctypes`)
VALUES {}'''.format(', '.join(['%s'] * len(new_user_permissions_list))), # nosec
tuple(new_user_permissions_list)
)
if user_permissions_to_delete:
frappe.db.sql('DELETE FROM `tabUser Permission` WHERE `name` IN ({})'.format( # nosec
','.join(['%s'] * len(user_permissions_to_delete))
), tuple(user_permissions_to_delete))

View File

@ -6,7 +6,7 @@ import frappe
def execute():
frappe.reload_doc('stock', 'doctype', 'item')
frappe.db.sql(""" update `tabItem` set allow_transfer_for_manufacture = 1
frappe.db.sql(""" update `tabItem` set include_item_in_manufacturing = 1
where ifnull(is_stock_item, 0) = 1""")
for doctype in ['BOM Item', 'Work Order Item', 'BOM Explosion Item']:
@ -14,7 +14,7 @@ def execute():
frappe.db.sql(""" update `tab{0}` child, tabItem item
set
child.allow_transfer_for_manufacture = 1
child.include_item_in_manufacturing = 1
where
child.item_code = item.name and ifnull(item.is_stock_item, 0) = 1
""".format(doctype))

View File

@ -32,7 +32,7 @@ def execute():
'user': user,
'default': 1
})
_doc.pos_profile_name = user + ' - ' + _doc.company
_doc.flags.ignore_validate = True
_doc.flags.ignore_mandatory = True
_doc.save()

View File

@ -11,14 +11,14 @@ def execute():
for pos in frappe.get_all(doctype, filters={'disabled': 0}):
doc = frappe.get_doc(doctype, pos.name)
if not doc.user or doc.pos_profile_name: continue
if not doc.user: continue
try:
doc.pos_profile_name = doc.user + ' - ' + doc.company
pos_profile_name = doc.user + ' - ' + doc.company
doc.flags.ignore_validate = True
doc.flags.ignore_mandatory = True
doc.save()
frappe.rename_doc(doctype, doc.name, doc.pos_profile_name, force=True)
frappe.rename_doc(doctype, doc.name, pos_profile_name, force=True)
except frappe.LinkValidationError:
frappe.db.set_value("POS Profile", doc.name, 'disabled', 1)

View File

@ -16,7 +16,7 @@ from six import iteritems
class Project(Document):
def get_feed(self):
return '{0}: {1}'.format(_(self.status), self.project_name)
return '{0}: {1}'.format(_(self.status), frappe.safe_decode(self.project_name))
def onload(self):
"""Load project tasks for quick view"""
@ -76,7 +76,7 @@ class Project(Document):
def validate_project_name(self):
if self.get("__islocal") and frappe.db.exists("Project", self.project_name):
frappe.throw(_("Project {0} already exists").format(self.project_name))
frappe.throw(_("Project {0} already exists").format(frappe.safe_decode(self.project_name)))
def validate_dates(self):
if self.expected_start_date and self.expected_end_date:
@ -258,13 +258,13 @@ class Project(Document):
self.total_purchase_cost = total_purchase_cost and total_purchase_cost[0][0] or 0
def update_sales_amount(self):
total_sales_amount = frappe.db.sql("""select sum(base_grand_total)
total_sales_amount = frappe.db.sql("""select sum(base_net_total)
from `tabSales Order` where project = %s and docstatus=1""", self.name)
self.total_sales_amount = total_sales_amount and total_sales_amount[0][0] or 0
def update_billed_amount(self):
total_billed_amount = frappe.db.sql("""select sum(base_grand_total)
total_billed_amount = frappe.db.sql("""select sum(base_net_total)
from `tabSales Invoice` where project = %s and docstatus=1""", self.name)
self.total_billed_amount = total_billed_amount and total_billed_amount[0][0] or 0

View File

@ -186,6 +186,8 @@ def make_salary_structure_for_timesheet(employee):
if not frappe.db.get_value("Salary Structure Assignment",
{'employee':employee, 'docstatus': 1}):
frappe.db.set_value('Employee', employee, 'date_of_joining',
add_months(nowdate(), -5))
create_salary_structure_assignment(employee, salary_structure.name)
return salary_structure

View File

@ -97,6 +97,9 @@ erpnext.setup.slides_settings = [
if (!this.values.company_abbr) {
return false;
}
if (this.values.company_abbr.length > 5) {
return false;
}
return true;
}
},

4
erpnext/public/js/utils.js Normal file → Executable file
View File

@ -126,7 +126,7 @@ $.extend(erpnext.utils, {
'</div><div class="col-xs-6 small" style="margin-bottom:10px">Annual Billing: <b>'
+format_currency(company_wise_info[0].billing_this_year, company_wise_info[0].currency)+'</b></div>' +
'<div class="col-xs-6 small" style="margin-bottom:10px">Total Unpaid: <b>'
+format_currency(company_wise_info[0].billing_this_year, company_wise_info[0].currency)+'</b></div>'
+format_currency(company_wise_info[0].total_unpaid, company_wise_info[0].currency)+'</b></div>'
);
}
}
@ -237,7 +237,7 @@ $.extend(erpnext.utils, {
let unscrub_option = frappe.model.unscrub(option);
let user_permission = frappe.defaults.get_user_permissions();
if(user_permission && user_permission[unscrub_option]) {
return user_permission[unscrub_option]["docs"];
return user_permission[unscrub_option].map(perm => perm.doc);
} else {
return $.map(locals[`:${unscrub_option}`], function(c) { return c.name; }).sort();
}

View File

@ -1,85 +1,75 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Fichier des Ecritures Comptables [FEC]"] = {
"filters": [{
"fieldname": "company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"default": frappe.defaults.get_user_default("Company"),
"reqd": 1
},
{
"fieldname": "fiscal_year",
"label": __("Fiscal Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"reqd": 1
}],
"filters": [
{
"fieldname": "company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"default": frappe.defaults.get_user_default("Company"),
"reqd": 1
},
{
"fieldname": "fiscal_year",
"label": __("Fiscal Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"reqd": 1
}
],
onload: function(query_report) {
query_report.page.add_inner_button(__("Export"), function() {
var fiscal_year = query_report.get_values().fiscal_year;
var company = query_report.get_values().company;
fec_export(query_report);
});
frappe.call({
method: "frappe.client.get_value",
args: {
'doctype': "Company",
'fieldname': ['siren_number'],
'filters': {
'name': company
}
},
callback: function(data) {
var company_data = data.message.siren_number;
if (company_data === null || company_data === undefined) {
frappe.msgprint(__("Please register the SIREN number in the company information file"))
} else {
frappe.call({
method: "frappe.client.get_value",
args: {
'doctype': "Fiscal Year",
'fieldname': ['year_end_date'],
'filters': {
'name': fiscal_year
}
},
callback: function(data) {
var fy = data.message.year_end_date;
var title = company_data + "FEC" + moment(fy).format('YYYYMMDD');
var result = $.map(frappe.slickgrid_tools.get_view_data(query_report.columns, query_report.dataView),
function(row) {
return [row.splice(1)];
});
downloadify(result, null, title);
}
});
query_report.add_make_chart_button = function() {
//
};
}
}
query_report.export_report = function() {
fec_export(query_report);
};
}
};
let fec_export = function(query_report) {
const fiscal_year = query_report.get_values().fiscal_year;
const company = query_report.get_values().company;
frappe.db.get_value("Company", company, "siren_number", (value) => {
const company_data = value.siren_number;
if (company_data === null || company_data === undefined) {
frappe.msgprint(__("Please register the SIREN number in the company information file"));
} else {
frappe.db.get_value("Fiscal Year", fiscal_year, "year_end_date", (r) => {
const fy = r.year_end_date;
const title = company_data + "FEC" + moment(fy).format('YYYYMMDD');
const column_row = query_report.columns.map(col => col.label);
const column_data = query_report.get_data_for_csv(false);
const result = [column_row].concat(column_data);
downloadify(result, null, title);
});
});
}
}
}
});
};
var downloadify = function(data, roles, title) {
let downloadify = function(data, roles, title) {
if (roles && roles.length && !has_common(roles, roles)) {
frappe.msgprint(__("Export not allowed. You need {0} role to export.", [frappe.utils.comma_or(roles)]));
return;
}
var filename = title + ".csv";
var csv_data = to_tab_csv(data);
var a = document.createElement('a');
const filename = title + ".txt";
let csv_data = to_tab_csv(data);
const a = document.createElement('a');
if ("download" in a) {
// Used Blob object, because it can handle large files
var blob_object = new Blob([csv_data], {
let blob_object = new Blob([csv_data], {
type: 'text/csv;charset=UTF-8'
});
a.href = URL.createObjectURL(blob_object);
@ -98,8 +88,8 @@ var downloadify = function(data, roles, title) {
document.body.removeChild(a);
};
var to_tab_csv = function(data) {
var res = [];
let to_tab_csv = function(data) {
let res = [];
$.each(data, function(i, row) {
res.push(row.join("\t"));
});

View File

@ -1,6 +1,11 @@
Selling management module. Includes forms for capturing / managing the sales process.
Selling management module. Includes forms for capturing / managing the sales process:
- Customer
- Campaign
- Quotation
- Sales Order
Moved to CRM Module:
- Lead
- Opportunity
- Quotation
- Sales Order

View File

@ -871,10 +871,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -993,6 +995,7 @@
"label": "Net Rate",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
@ -1910,7 +1913,7 @@
"istable": 1,
"max_attachments": 0,
"menu_index": 0,
"modified": "2018-08-22 16:15:52.750381",
"modified": "2018-12-12 05:52:46.135944",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation Item",
@ -1925,4 +1928,4 @@
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}
}

View File

@ -623,7 +623,7 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
def update_item(source, target, source_parent):
target.amount = flt(source.amount) - flt(source.billed_amt)
target.base_amount = target.amount * flt(source_parent.conversion_rate)
target.qty = target.amount / flt(source.rate) if (source.rate and source.billed_amt) else source.qty
target.qty = target.amount / flt(source.rate) if (source.rate and source.billed_amt) else source.qty - source.returned_qty
if source_parent.project:
target.cost_center = frappe.db.get_value("Project", source_parent.project, "cost_center")

View File

@ -394,7 +394,7 @@ erpnext.pos.PointOfSale = class PointOfSale {
}
}
frappe.prompt(this.get_promopt_fields(),
frappe.prompt(this.get_prompt_fields(),
on_submit,
__('Select POS Profile')
);
@ -417,11 +417,12 @@ erpnext.pos.PointOfSale = class PointOfSale {
]);
}
get_promopt_fields() {
get_prompt_fields() {
return [{
fieldtype: 'Link',
label: __('POS Profile'),
options: 'POS Profile',
fieldname: 'pos_profile',
reqd: 1,
get_query: () => {
return {
@ -433,7 +434,8 @@ erpnext.pos.PointOfSale = class PointOfSale {
}
}, {
fieldtype: 'Check',
label: __('Set as default')
label: __('Set as default'),
fieldname: 'set_as_default'
}];
}
@ -522,6 +524,7 @@ erpnext.pos.PointOfSale = class PointOfSale {
this.frm.meta.default_print_format = r.message.print_format || "";
this.frm.allow_edit_rate = r.message.allow_edit_rate;
this.frm.allow_edit_discount = r.message.allow_edit_discount;
this.frm.doc.campaign = r.message.campaign;
}
}
@ -1128,12 +1131,15 @@ class POSItems {
this.events = events;
this.currency = this.frm.doc.currency;
this.make_dom();
this.make_fields();
frappe.db.get_value("Item Group", {lft: 1, is_group: 1}, "name", (r) => {
this.parent_item_group = r.name;
this.make_dom();
this.make_fields();
this.init_clusterize();
this.bind_events();
this.load_items_data();
this.init_clusterize();
this.bind_events();
this.load_items_data();
})
}
load_items_data() {
@ -1175,6 +1181,7 @@ class POSItems {
make_fields() {
// Search field
const me = this;
this.search_field = frappe.ui.form.make_control({
df: {
fieldtype: 'Data',
@ -1202,7 +1209,7 @@ class POSItems {
fieldtype: 'Link',
label: 'Item Group',
options: 'Item Group',
default: 'All Item Groups',
default: me.parent_item_group,
onchange: () => {
const item_group = this.item_group_field.get_value();
if (item_group) {
@ -1258,7 +1265,7 @@ class POSItems {
this.clusterize.update(row_items);
}
filter_items({ search_term='', item_group='All Item Groups' }={}) {
filter_items({ search_term='', item_group=this.parent_item_group }={}) {
if (search_term) {
search_term = search_term.toLowerCase();
@ -1271,7 +1278,7 @@ class POSItems {
this.set_item_in_the_cart(items);
return;
}
} else if (item_group == "All Item Groups") {
} else if (item_group == this.parent_item_group) {
this.items = this.all_items;
return this.render_items(this.all_items);
}
@ -1376,7 +1383,7 @@ class POSItems {
return template;
}
get_items({start = 0, page_length = 40, search_value='', item_group="All Item Groups"}={}) {
get_items({start = 0, page_length = 40, search_value='', item_group=this.parent_item_group}={}) {
return new Promise(res => {
frappe.call({
method: "erpnext.selling.page.point_of_sale.point_of_sale.get_items",

View File

@ -0,0 +1,6 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Pending SO Items For Purchase Request"] = {
}

View File

@ -1,36 +1,35 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2013-06-21 16:46:45",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"is_standard": "Yes",
"modified": "2017-02-24 20:08:11.744036",
"modified_by": "Administrator",
"module": "Selling",
"name": "Pending SO Items For Purchase Request",
"owner": "Administrator",
"query": "select so_item.item_code as \"Item Code:Link/Item:120\",\n so_item.item_name as \"Item Name::120\",\n so_item.description as \"Description::120\",\n so.`name` as \"S.O. No.:Link/Sales Order:120\",\n so.`transaction_date` as \"Date:Date:120\",\n mr.name as \"Material Request:Link/Material Request:120\",\n so.customer as \"Customer:Link/Customer:120\",\n so.territory as \"Terretory:Link/Territory:120\",\n sum(so_item.qty) as \"SO Qty:Float:100 \",\n sum(mr_item.qty) as \"Requested Qty:Float:100\",\n sum(so_item.qty) - sum(mr_item.qty) as \"Pending Qty:Float:100 \", \n so.company as \"Company:Link/Company:\"\nfrom\n `tabSales Order` so, `tabSales Order Item` so_item, \n `tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere \n so_item.`parent` = so.`name` \n and mr_item.parent = mr.name\n and mr_item.sales_order = so.name\n and mr_item.item_code = so_item.item_code\n and so.docstatus = 1 and so.status != \"Closed\" \n and mr.docstatus = 1 and mr.status != \"Stopped\"\ngroup by so.name, so_item.item_code\nhaving sum(so_item.qty) > sum(mr_item.qty)\norder by so.name desc, so_item.item_code asc",
"ref_doctype": "Sales Order",
"report_name": "Pending SO Items For Purchase Request",
"report_type": "Query Report",
"add_total_row": 0,
"creation": "2018-11-12 14:08:27.241332",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2018-11-12 14:08:27.241332",
"modified_by": "Administrator",
"module": "Selling",
"name": "Pending SO Items For Purchase Request",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Sales Order",
"report_name": "Pending SO Items For Purchase Request",
"report_type": "Script Report",
"roles": [
{
"role": "Sales User"
},
"role": "Stock User"
},
{
"role": "Sales Manager"
},
},
{
"role": "Maintenance User"
},
},
{
"role": "Accounts User"
},
},
{
"role": "Stock User"
"role": "Sales User"
}
]
}

View File

@ -0,0 +1,148 @@
# 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 frappe.utils import cint,cstr
def execute(filters=None):
columns = get_columns()
data = get_data()
return columns, data
def get_columns():
columns = [
{
"label": _("Item Code"),
"options": "Item",
"fieldname": "item_code",
"fieldtype": "Link",
"width": 200
},
{
"label": _("Item Name"),
"fieldname": "item_name",
"fieldtype": "Data",
"width": 200
},
{
"label": _("Description"),
"fieldname": "description",
"fieldtype": "Data",
"width": 140
},
{
"label": _("S.O. No."),
"options": "Sales Order",
"fieldname": "sales_order_no",
"fieldtype": "Link",
"width": 140
},
{
"label": _("Date"),
"fieldname": "date",
"fieldtype": "Date",
"width": 140
},
{
"label": _("Material Request"),
"options": "Material Request",
"fieldname": "material_request",
"fieldtype": "Link",
"width": 140
},
{
"label": _("Customer"),
"fieldname": "customer",
"fieldtype": "Data",
"width": 140
},
{
"label": _("Territory"),
"fieldname": "territory",
"fieldtype": "Data",
"width": 140
},
{
"label": _("SO Qty"),
"fieldname": "so_qty",
"fieldtype": "Float",
"width": 140
},
{
"label": _("Requested Qty"),
"fieldname": "requested_qty",
"fieldtype": "Float",
"width": 140
},
{
"label": _("Pending Qty"),
"fieldname": "pending_qty",
"fieldtype": "Float",
"width": 140
},
{
"label": _("Company"),
"fieldname": "company",
"fieldtype": "Data",
"width": 140
}
]
return columns
def get_data():
sales_order_entry = frappe.db.sql("""
SELECT
so_item.item_code,
so_item.item_name,
so_item.description,
so.name,
so.transaction_date,
so.customer,
so.territory,
sum(so_item.qty) as net_qty,
so.company
FROM `tabSales Order` so, `tabSales Order Item` so_item
WHERE
so.docstatus = 1
and so.name = so_item.parent
and so.status not in ("Closed","Completed","Cancelled")
GROUP BY
so.name,so_item.item_code
""", as_dict = 1)
mr_records = frappe.get_all("Material Request Item",
{"sales_order_item": ("!=",""), "docstatus": 1},
["parent", "qty", "sales_order", "item_code"])
grouped_records = {}
for record in mr_records:
grouped_records.setdefault(record.sales_order, []).append(record)
pending_so=[]
for so in sales_order_entry:
# fetch all the material request records for a sales order item
mr_list = grouped_records.get(so.name) or [{}]
mr_item_record = ([mr for mr in mr_list if mr.get('item_code') == so.item_code] or [{}])
for mr in mr_item_record:
# check for pending sales order
if cint(so.net_qty) > cint(mr.get('qty')):
so_record = {
"item_code": so.item_code,
"item_name": so.item_name,
"description": so.description,
"sales_order_no": so.name,
"date": so.transaction_date,
"material_request": cstr(mr.get('parent')),
"customer": so.customer,
"territory": so.territory,
"so_qty": so.net_qty,
"requested_qty": cint(mr.get('qty')),
"pending_qty": so.net_qty - cint(mr.get('qty')),
"company": so.company
}
pending_so.append(so_record)
return pending_so

View File

@ -0,0 +1,27 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import unittest
from frappe.utils import nowdate, add_months
from erpnext.selling.report.pending_so_items_for_purchase_request.pending_so_items_for_purchase_request\
import execute
from erpnext.selling.doctype.sales_order.sales_order import make_material_request
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
class TestPendingSOItemsForPurchaseRequest(unittest.TestCase):
def test_result_for_partial_material_request(self):
so = make_sales_order()
mr=make_material_request(so.name)
mr.items[0].qty = 4
mr.schedule_date = add_months(nowdate(),1)
mr.submit()
report = execute()
l = len(report[1])
self.assertEqual((so.items[0].qty - mr.items[0].qty), report[1][l-1]['pending_qty'])
def test_result_for_so_item(self):
so = make_sales_order()
report = execute()
l = len(report[1])
self.assertEqual(so.items[0].qty, report[1][l-1]['pending_qty'])

View File

@ -76,10 +76,21 @@ frappe.query_reports["Sales Analytics"] = {
events: {
onCheckRow: function(data) {
row_name = data[2].content;
length = data.length
row_values = data.slice(4,length-1).map(function (column) {
return column.content;
})
length = data.length;
var tree_type = frappe.query_report.filters[0].value;
if(tree_type == "Customer" || tree_type == "Item") {
row_values = data.slice(4,length-1).map(function (column) {
return column.content;
})
}
else {
row_values = data.slice(3,length-1).map(function (column) {
return column.content;
})
}
entry = {
'name':row_name,
'values':row_values

View File

@ -276,7 +276,11 @@ class Analytics(object):
def get_chart_data(self):
length = len(self.columns)
labels = [d.get("label") for d in self.columns[2:length-1]]
if self.filters.tree_type in ["Customer", "Supplier", "Item"]:
labels = [d.get("label") for d in self.columns[2:length-1]]
else:
labels = [d.get("label") for d in self.columns[1:length-1]]
self.chart = {
"data": {
'labels': labels,

View File

@ -391,8 +391,24 @@ def get_invoiced_qty_map(delivery_note):
return invoiced_qty_map
def get_returned_qty_map(sales_orders):
"""returns a map: {so_detail: returned_qty}"""
returned_qty_map = {}
for name, returned_qty in frappe.get_all('Sales Order Item', fields = ["name", "returned_qty"],
filters = {'parent': ('in', sales_orders), 'docstatus': 1}, as_list=1):
if not returned_qty_map.get(name):
returned_qty_map[name] = 0
returned_qty_map[name] += returned_qty
return returned_qty_map
@frappe.whitelist()
def make_sales_invoice(source_name, target_doc=None):
doc = frappe.get_doc('Delivery Note', source_name)
sales_orders = [d.against_sales_order for d in doc.items]
returned_qty_map = get_returned_qty_map(sales_orders)
invoiced_qty_map = get_invoiced_qty_map(source_name)
def set_missing_values(source, target):
@ -412,7 +428,9 @@ def make_sales_invoice(source_name, target_doc=None):
target.update(get_fetch_values("Sales Invoice", 'company_address', target.company_address))
def update_item(source_doc, target_doc, source_parent):
target_doc.qty = source_doc.qty - invoiced_qty_map.get(source_doc.name, 0)
target_doc.qty = (source_doc.qty -
invoiced_qty_map.get(source_doc.name, 0) - returned_qty_map.get(source_doc.so_detail, 0))
if source_doc.serial_no and source_parent.per_billed > 0:
target_doc.serial_no = get_delivery_note_serial_no(source_doc.item_code,
target_doc.qty, source_parent.name)

View File

@ -1,7 +1,8 @@
frappe.listview_settings['Delivery Note'] = {
add_fields: ["grand_total", "is_return", "per_billed", "status"],
get_indicator: function (doc) {
if (cint(doc.is_return) == 1) {
add_fields: ["customer", "customer_name", "base_grand_total", "per_installed", "per_billed",
"transporter_name", "grand_total", "is_return", "status", "currency"],
get_indicator: function(doc) {
if(cint(doc.is_return)==1) {
return [__("Return"), "darkgrey", "is_return,=,Yes"];
} else if (doc.status === "Closed") {
return [__("Closed"), "green", "status,=,Closed"];

View File

@ -636,6 +636,24 @@ class TestDeliveryNote(unittest.TestCase):
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
set_perpetual_inventory(0, company)
def test_make_sales_invoice_from_dn_for_returned_qty(self):
from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
so = make_sales_order(qty=2)
so.submit()
dn = make_delivery_note(so.name)
dn.submit()
dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-1, do_not_submit=True)
dn1.items[0].against_sales_order = so.name
dn1.items[0].so_detail = so.items[0].name
dn1.submit()
si = make_sales_invoice(dn.name)
self.assertEquals(si.items[0].qty, 1)
def create_delivery_note(**args):
dn = frappe.new_doc("Delivery Note")

View File

@ -651,7 +651,7 @@ $.extend(erpnext.item, {
frappe.call({
method:"erpnext.stock.doctype.item.item.get_item_attribute",
args:{
parent: "Item Attribute",
parent: i,
attribute_value: term
},
callback: function(r) {

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
@ -453,7 +454,7 @@
"collapsible": 0,
"columns": 0,
"default": "1",
"fieldname": "allow_transfer_for_manufacture",
"fieldname": "include_item_in_manufacturing",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
@ -462,7 +463,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Transfer for Manufacture",
"label": "Include Item In Manufacturing",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -1459,7 +1460,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.has_batch_no==1 && doc.create_new_batch==1",
"description": "Example: ABCD.#####. If series is set and Batch No is not mentioned in transactions, then automatic batch number will be created based on this series. If you always want to explicitly mention Batch No for this item, leave this blank. Note: this setting will take priority over the Naming Series Prefix in Stock Settings.",
"description": "Example: ABCD.#####. If series is set and Batch No is not mentioned in transactions,then automatic batch number will be created based on this series. If you always want to explicitly mention Batch No for this item,leave this blank. Note: this setting will take priority over the Naming Series Prefix in Stock Settings.",
"fieldname": "batch_number_series",
"fieldtype": "Data",
"hidden": 0,
@ -1661,7 +1662,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.is_stock_item || doc.is_fixed_asset",
"description": "Example: ABCD.#####\nIf series is set and Serial No is not mentioned in transactions, then automatic serial number will be created based on this series. If you always want to explicitly mention Serial Nos for this item. leave this blank.",
"description": "Example: ABCD.#####\nIf series is set and Serial No is not mentioned in transactions,then automatic serial number will be created based on this series. If you always want to explicitly mention Serial Nos for this item. leave this blank.",
"fieldname": "serial_no_series",
"fieldtype": "Data",
"hidden": 0,
@ -1729,7 +1730,7 @@
"columns": 0,
"default": "0",
"depends_on": "eval:!doc.variant_of",
"description": "If this item has variants, then it cannot be selected in sales orders etc.",
"description": "If this item has variants,then it cannot be selected in sales orders etc.",
"fieldname": "has_variants",
"fieldtype": "Check",
"hidden": 0,
@ -1841,7 +1842,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sales, Purchase, Accounting Defaults",
"label": "Sales,Purchase,Accounting Defaults",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -4113,7 +4114,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 1,
"modified": "2018-09-20 11:14:21.031369",
"modified": "2018-11-20 19:04:22.568410",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",

View File

@ -392,4 +392,3 @@ def create_item(item_code, is_stock_item=None, valuation_rate=0, warehouse=None)
"company": "_Test Company"
})
item.save()

View File

@ -1,463 +1,464 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-05-03 02:29:24.444341",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-05-03 02:29:24.444341",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"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": "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,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"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,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_warehouse",
"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": "Default Warehouse",
"length": 0,
"no_copy": 0,
"options": "Warehouse",
"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,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_warehouse",
"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": "Default Warehouse",
"length": 0,
"no_copy": 0,
"options": "Warehouse",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"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,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"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,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_price_list",
"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": "Default Price List",
"length": 0,
"no_copy": 0,
"options": "Price List",
"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,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_price_list",
"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": "Default Price List",
"length": 0,
"no_copy": 0,
"options": "Price List",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "purchase_defaults",
"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": "Purchase Defaults",
"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,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "purchase_defaults",
"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": "Purchase Defaults",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "buying_cost_center",
"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": "Default Buying Cost Center",
"length": 0,
"no_copy": 0,
"options": "Cost Center",
"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,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "buying_cost_center",
"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": "Default Buying Cost Center",
"length": 0,
"no_copy": 0,
"options": "Cost Center",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_supplier",
"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": "Default Supplier",
"length": 0,
"no_copy": 0,
"options": "Supplier",
"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,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_supplier",
"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": "Default Supplier",
"length": 0,
"no_copy": 0,
"options": "Supplier",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_8",
"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,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_8",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "expense_account",
"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": "Default Expense Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"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,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "expense_account",
"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": "Default Expense Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "selling_defaults",
"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": "Sales Defaults",
"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,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "selling_defaults",
"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": "Sales Defaults",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "selling_cost_center",
"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": "Default Selling Cost Center",
"length": 0,
"no_copy": 0,
"options": "Cost Center",
"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,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "selling_cost_center",
"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": "Default Selling Cost Center",
"length": 0,
"no_copy": 0,
"options": "Cost Center",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_12",
"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,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_12",
"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_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "income_account",
"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": "Default Income Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"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,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "income_account",
"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": "Default Income Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"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
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-09-19 16:17:52.562232",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Default",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-12-07 11:48:07.638935",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Default",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -441,31 +441,40 @@ def raise_work_orders(material_request):
errors =[]
work_orders = []
default_wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse")
for d in mr.items:
if (d.qty - d.ordered_qty) >0:
if frappe.db.get_value("BOM", {"item": d.item_code, "is_default": 1}):
if frappe.db.exists("BOM", {"item": d.item_code, "is_default": 1}):
wo_order = frappe.new_doc("Work Order")
wo_order.production_item = d.item_code
wo_order.qty = d.qty - d.ordered_qty
wo_order.fg_warehouse = d.warehouse
wo_order.wip_warehouse = default_wip_warehouse
wo_order.description = d.description
wo_order.stock_uom = d.stock_uom
wo_order.expected_delivery_date = d.schedule_date
wo_order.sales_order = d.sales_order
wo_order.bom_no = get_item_details(d.item_code).bom_no
wo_order.material_request = mr.name
wo_order.material_request_item = d.name
wo_order.planned_start_date = mr.transaction_date
wo_order.company = mr.company
wo_order.update({
"production_item": d.item_code,
"qty": d.qty - d.ordered_qty,
"fg_warehouse": d.warehouse,
"wip_warehouse": default_wip_warehouse,
"description": d.description,
"stock_uom": d.stock_uom,
"expected_delivery_date": d.schedule_date,
"sales_order": d.sales_order,
"bom_no": get_item_details(d.item_code).bom_no,
"material_request": mr.name,
"material_request_item": d.name,
"planned_start_date": mr.transaction_date,
"company": mr.company
})
wo_order.set_work_order_operations()
wo_order.save()
work_orders.append(wo_order.name)
else:
errors.append(_("Row {0}: Bill of Materials not found for the Item {1}").format(d.idx, d.item_code))
if work_orders:
message = ["""<a href="#Form/Work Order/%s" target="_blank">%s</a>""" % \
(p, p) for p in work_orders]
msgprint(_("The following Work Orders were created:") + '\n' + new_line_sep(message))
if errors:
frappe.throw(_("Productions Orders cannot be raised for:") + '\n' + new_line_sep(errors))
return work_orders

View File

@ -1,7 +1,8 @@
frappe.listview_settings['Purchase Receipt'] = {
add_fields: ["is_return", "grand_total", "status", "per_billed"],
get_indicator: function (doc) {
if (cint(doc.is_return) == 1) {
add_fields: ["supplier", "supplier_name", "base_grand_total", "is_subcontracted",
"transporter_name", "is_return", "status", "per_billed", "currency"],
get_indicator: function(doc) {
if(cint(doc.is_return)==1) {
return [__("Return"), "darkgrey", "is_return,=,Yes"];
} else if (doc.status === "Closed") {
return [__("Closed"), "green", "status,=,Closed"];

View File

@ -61,7 +61,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters):
if filters.get("from"):
from frappe.desk.reportview import get_match_cond
mcond = get_match_cond(filters["from"])
cond = ""
cond, qi_condition = "", "and (quality_inspection is null or quality_inspection = '')"
if filters.get('from') in ['Purchase Invoice Item', 'Purchase Receipt Item']:
cond = """and item_code in (select name from `tabItem` where
@ -72,9 +72,13 @@ def item_query(doctype, txt, searchfield, start, page_len, filters):
elif filters.get('from') == 'Stock Entry Detail':
cond = """and s_warehouse is null"""
if filters.get('from') in ['Supplier Quotation Item']:
qi_condition = ""
return frappe.db.sql(""" select item_code from `tab{doc}`
where parent=%(parent)s and docstatus < 2 and item_code like %(txt)s
and (quality_inspection is null or quality_inspection = '')
{cond} {mcond} order by item_code limit {start}, {page_len}""".format(doc=filters.get('from'),
parent=filters.get('parent'), cond=cond, mcond=mcond, start=start, page_len = page_len),
{qi_condition} {cond} {mcond}
order by item_code limit {start}, {page_len}""".format(doc=filters.get('from'),
parent=filters.get('parent'), cond = cond, mcond = mcond, start = start,
page_len = page_len, qi_condition = qi_condition),
{'parent': filters.get('parent'), 'txt': "%%%s%%" % txt})

View File

@ -3,8 +3,45 @@
import frappe
import unittest
from frappe.utils import nowdate
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
from erpnext.controllers.stock_controller import QualityInspectionRejectedError, QualityInspectionRequiredError
# test_records = frappe.get_test_records('Quality Inspection')
class TestQualityInspection(unittest.TestCase):
pass
def setUp(self):
create_item("_Test Item with QA")
frappe.db.set_value("Item", "_Test Item with QA", "inspection_required_before_delivery", 1)
def test_qa_for_delivery(self):
dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
self.assertRaises(QualityInspectionRequiredError, dn.submit)
qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, status="Rejected")
dn.reload()
self.assertRaises(QualityInspectionRejectedError, dn.submit)
frappe.db.set_value("Quality Inspection Reading", {"parent": qa.name}, "status", "Accepted")
dn.reload()
dn.submit()
def create_quality_inspection(**args):
args = frappe._dict(args)
qa = frappe.new_doc("Quality Inspection")
qa.report_date = nowdate()
qa.inspection_type = args.inspection_type or "Outgoing"
qa.reference_type = args.reference_type
qa.reference_name = args.reference_name
qa.item_code = args.item_code or "_Test Item with QA"
qa.sample_size = 1
qa.inspected_by = frappe.session.user
qa.append("readings", {
"specification": "Size",
"status": args.status
})
qa.save()
qa.submit()
return qa

View File

@ -605,7 +605,7 @@ class StockEntry(StockController):
if self.job_card:
job_doc = frappe.get_doc('Job Card', self.job_card)
job_doc.set_transferred_qty()
job_doc.set_transferred_qty(update_status=True)
if self.work_order:
pro_doc = frappe.get_doc("Work Order", self.work_order)
@ -999,7 +999,7 @@ class StockEntry(StockController):
for d in pro_order.get("required_items"):
if (flt(d.required_qty) > flt(d.transferred_qty) and
(d.allow_transfer_for_manufacture or self.purpose != "Material Transfer for Manufacture")):
(d.include_item_in_manufacturing or self.purpose != "Material Transfer for Manufacture")):
item_row = d.as_dict()
if d.source_warehouse and not frappe.db.get_value("Warehouse", d.source_warehouse, "is_group"):
item_row["from_warehouse"] = d.source_warehouse

View File

@ -42,7 +42,7 @@ frappe.ui.form.on("Stock Reconciliation", {
},
get_items: function(frm) {
frappe.prompt({label:"Warehouse", fieldtype:"Link", options:"Warehouse", reqd: 1,
frappe.prompt({label:"Warehouse", fieldname: "warehouse", fieldtype:"Link", options:"Warehouse", reqd: 1,
"get_query": function() {
return {
"filters": {

View File

@ -10,7 +10,7 @@ frappe.query_reports["Warehouse wise Item Balance Age and Value"] = {
"fieldtype": "Date",
"width": "80",
"reqd": 1,
"default": frappe.sys_defaults.year_start_date,
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
},
{
"fieldname":"to_date",

View File

@ -8,7 +8,8 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt, cint, getdate
from erpnext.stock.report.stock_balance.stock_balance import get_item_details, get_item_reorder_details, get_item_warehouse_map
from erpnext.stock.report.stock_balance.stock_balance import (get_item_details,
get_item_reorder_details, get_item_warehouse_map, get_items, get_stock_ledger_entries)
from erpnext.stock.report.stock_ageing.stock_ageing import get_fifo_queue, get_average_age
from six import iteritems
@ -18,8 +19,12 @@ def execute(filters=None):
validate_filters(filters)
columns = get_columns(filters)
item_map = get_item_details(filters)
iwb_map = get_item_warehouse_map(filters)
items = get_items(filters)
sle = get_stock_ledger_entries(filters, items)
item_map = get_item_details(items, sle, filters)
iwb_map = get_item_warehouse_map(filters, sle)
warehouse_list = get_warehouse_list(filters)
item_ageing = get_fifo_queue(filters)
data = []
@ -27,6 +32,8 @@ def execute(filters=None):
item_value = {}
for (company, item, warehouse) in sorted(iwb_map):
if not item_map.get(item): continue
row = []
qty_dict = iwb_map[(company, item, warehouse)]
item_balance.setdefault((item, item_map[item]["item_group"]), [])
@ -42,6 +49,8 @@ def execute(filters=None):
# sum bal_qty by item
for (item, item_group), wh_balance in iteritems(item_balance):
if not item_ageing.get(item): continue
total_stock_value = sum(item_value[(item, item_group)])
row = [item, item_group, total_stock_value]
@ -85,11 +94,10 @@ def validate_filters(filters):
filters["company"] = frappe.defaults.get_user_default("Company")
def get_warehouse_list(filters):
from frappe.defaults import get_user_permissions
from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
condition = ''
user_permitted_warehouse = filter(None, get_user_permissions()
.get("Warehouse", {})
.get("docs", []))
user_permitted_warehouse = get_permitted_documents('Warehouse')
value = ()
if user_permitted_warehouse:
condition = "and name in %s"

View File

@ -16,7 +16,7 @@
<tr>
<td>{{ item }}</td>
<td class='text-right'>
{{ frappe.utils.fmt_money(itemised_taxable_amount.get(item), None, currency) }}
{{ frappe.utils.fmt_money(itemised_taxable_amount.get(item, 0), None, currency) }}
</td>
{% for tax_account in tax_accounts %}
{% set tax_details = taxes.get(tax_account) %}

View File

@ -26,7 +26,6 @@ def get_product_list(search=None, start=0, limit=12):
left join tabBin S on I.item_code = S.item_code and I.website_warehouse = S.warehouse
where (I.show_in_website = 1)
and I.disabled = 0
and (I.variant_of is null or I.variant_of='')
and (I.end_of_life is null or I.end_of_life='0000-00-00' or I.end_of_life > %(today)s)"""
# search term condition

View File

@ -146,8 +146,18 @@ class TransactionBase(StatusUpdater):
return ret
def delete_events(ref_type, ref_name):
frappe.delete_doc("Event", frappe.db.sql_list("""select name from `tabEvent`
where ref_type=%s and ref_name=%s""", (ref_type, ref_name)), for_reload=True)
events = frappe.db.sql_list(""" SELECT
distinct `tabEvent`.name
from
`tabEvent`, `tabEvent Participants`
where
`tabEvent`.name = `tabEvent Participants`.parent
and `tabEvent Participants`.reference_doctype = %s
and `tabEvent Participants`.reference_docname = %s
""", (ref_type, ref_name)) or []
if events:
frappe.delete_doc("Event", events, for_reload=True)
def validate_uom_is_integer(doc, uom_field, qty_fields, child_dt=None):
if isinstance(qty_fields, string_types):