Compare commits
205 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afdd7a626c | ||
|
|
7699b5e92a | ||
|
|
b5d765d19a | ||
|
|
6d03790b44 | ||
|
|
3638250f5d | ||
|
|
7bf192e46a | ||
|
|
fa351f4bdf | ||
|
|
f3f0dfef2a | ||
|
|
5d5a81f375 | ||
|
|
02c8607d04 | ||
|
|
ddd4845420 | ||
|
|
63b06420e1 | ||
|
|
bf66d7e041 | ||
|
|
fc85768685 | ||
|
|
37c784eb3a | ||
|
|
a54eb7d022 | ||
|
|
48af4289d3 | ||
|
|
03c30a7f92 | ||
|
|
949a920022 | ||
|
|
852cb64e4f | ||
|
|
b66fb9a4c0 | ||
|
|
9fbdf14e61 | ||
|
|
4b7a30b527 | ||
|
|
5ad4a6e161 | ||
|
|
10a4a13e5e | ||
|
|
3ceab64bfa | ||
|
|
6015f0f2ec | ||
|
|
e5ca48a76a | ||
|
|
3e4bcfe2d8 | ||
|
|
5e4c8ecd62 | ||
|
|
11df066ed0 | ||
|
|
87f2848aeb | ||
|
|
12450fa473 | ||
|
|
67bbcf6932 | ||
|
|
79918c66c2 | ||
|
|
960208bae0 | ||
|
|
1bad1fcc00 | ||
|
|
43ef4e9047 | ||
|
|
65f6f2a05e | ||
|
|
558fcc140f | ||
|
|
ea92682cfd | ||
|
|
a06a600d0a | ||
|
|
af9d3a4db6 | ||
|
|
f39a9f1c5b | ||
|
|
067991ee30 | ||
|
|
1589ada04d | ||
|
|
ee368221cf | ||
|
|
6691856d08 | ||
|
|
53de2f1b57 | ||
|
|
22aaf0cbd0 | ||
|
|
8ebc0df65f | ||
|
|
4095915bff | ||
|
|
abe8e2ecce | ||
|
|
b053674781 | ||
|
|
bb68f8ca01 | ||
|
|
943637e06c | ||
|
|
0407cb0d92 | ||
|
|
d192a420d1 | ||
|
|
3856d14ae6 | ||
|
|
85480b3af7 | ||
|
|
c4125b32ee | ||
|
|
4e08dfc72f | ||
|
|
53e34f5c1c | ||
|
|
c6e1c3c048 | ||
|
|
0c2de6e07a | ||
|
|
b801357ecf | ||
|
|
1508267fd6 | ||
|
|
8f1f93603d | ||
|
|
f1bd39c937 | ||
|
|
800c69eec3 | ||
|
|
ec252c806f | ||
|
|
2a903a0608 | ||
|
|
71b4e48ea5 | ||
|
|
ae2d92ee76 | ||
|
|
21cf1fd851 | ||
|
|
02c281cc2f | ||
|
|
a938d3956a | ||
|
|
02b0ed4199 | ||
|
|
76f93d05b7 | ||
|
|
24ab20fe11 | ||
|
|
1eb31db71f | ||
|
|
ef95b4d6e3 | ||
|
|
514c0417dd | ||
|
|
67f6ac848c | ||
|
|
5d95ebec4e | ||
|
|
bb7317d398 | ||
|
|
550268eaf3 | ||
|
|
7f9af46da5 | ||
|
|
e38eb83358 | ||
|
|
af00c9f70b | ||
|
|
5c7545da0c | ||
|
|
d6e8bb5452 | ||
|
|
435032f5bc | ||
|
|
1ef50c89db | ||
|
|
2e4b4454b3 | ||
|
|
4b33b7e1c1 | ||
|
|
69279229cc | ||
|
|
5b7028c7bb | ||
|
|
0018db344c | ||
|
|
e9a4f4e70d | ||
|
|
be03de3ad6 | ||
|
|
d235325a95 | ||
|
|
6daa6e422e | ||
|
|
07913c3ff2 | ||
|
|
71ba7ea717 | ||
|
|
3b0e6cecd6 | ||
|
|
544de60d36 | ||
|
|
d652221071 | ||
|
|
4d5d3b7685 | ||
|
|
3bf9071598 | ||
|
|
46af07cc09 | ||
|
|
27692670b1 | ||
|
|
3708df61bf | ||
|
|
3f2dd04b10 | ||
|
|
fd598d8f3a | ||
|
|
4e0999dda7 | ||
|
|
eef3f62ff7 | ||
|
|
104eb8c6ad | ||
|
|
3959c7c3c1 | ||
|
|
b43f4073db | ||
|
|
01659271be | ||
|
|
c616a4a527 | ||
|
|
eb7eb43dfc | ||
|
|
00ae424cac | ||
|
|
7b6e09b9a7 | ||
|
|
90a3e60de7 | ||
|
|
f3e91e2b2b | ||
|
|
846f5d4bd8 | ||
|
|
51520f9de6 | ||
|
|
9d457d3e34 | ||
|
|
8ddd946dea | ||
|
|
c20abf6b2a | ||
|
|
68a48c9cfc | ||
|
|
8c3f99f12e | ||
|
|
9b09ff29d2 | ||
|
|
f4e908bd91 | ||
|
|
5d8803b23e | ||
|
|
f970ca4d35 | ||
|
|
29cebbb0cd | ||
|
|
0b078fb4cb | ||
|
|
89d001caf3 | ||
|
|
6837e69187 | ||
|
|
5824ae98d9 | ||
|
|
67526f244e | ||
|
|
67cbeb1bb0 | ||
|
|
8859eb23a3 | ||
|
|
ae450fc23b | ||
|
|
82e816054e | ||
|
|
ea7768d3f3 | ||
|
|
cb650f836e | ||
|
|
f4fc30a72d | ||
|
|
095701e86b | ||
|
|
dccceb44f9 | ||
|
|
5f389c999a | ||
|
|
29fa0a9a17 | ||
|
|
c1d22adb9f | ||
|
|
bce13cd0e3 | ||
|
|
8623166dca | ||
|
|
577e4c4e28 | ||
|
|
210b6f8f7e | ||
|
|
25366a22ad | ||
|
|
919a74ad88 | ||
|
|
b3c8f44b3e | ||
|
|
94500fd17b | ||
|
|
dcd54209fc | ||
|
|
759f669214 | ||
|
|
70eca9462b | ||
|
|
60d9446656 | ||
|
|
934105bf34 | ||
|
|
26b8f5c3ce | ||
|
|
36693e27be | ||
|
|
a5d5403bf3 | ||
|
|
0e35651e94 | ||
|
|
837ffbddbc | ||
|
|
ff25573558 | ||
|
|
82a199d6d7 | ||
|
|
d15a507567 | ||
|
|
01e79f033e | ||
|
|
9a6e675b41 | ||
|
|
7331f169fc | ||
|
|
d09f35fa98 | ||
|
|
52d703a232 | ||
|
|
e4e00d2215 | ||
|
|
60944e3397 | ||
|
|
0b642cd414 | ||
|
|
b4de1c771b | ||
|
|
aedaac63ea | ||
|
|
96e629a635 | ||
|
|
ac3ad0810a | ||
|
|
1e96b7bbe5 | ||
|
|
0ea5d941e6 | ||
|
|
881491cd2b | ||
|
|
2c77165fc6 | ||
|
|
6561b8ade9 | ||
|
|
840c757b12 | ||
|
|
7055446508 | ||
|
|
e0434ad40e | ||
|
|
d53dd7f4c9 | ||
|
|
fed9816213 | ||
|
|
a35839aa47 | ||
|
|
5491275b66 | ||
|
|
70fe968f02 | ||
|
|
a3aa6a4449 | ||
|
|
8fa1e03367 | ||
|
|
0f0b121669 |
@@ -131,6 +131,7 @@
|
||||
"getCookies": true,
|
||||
"get_url_arg": true,
|
||||
"get_server_fields": true,
|
||||
"set_multiple": true
|
||||
"set_multiple": true,
|
||||
"QUnit": true
|
||||
}
|
||||
}
|
||||
|
||||
46
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@frappe.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
@@ -2,7 +2,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
__version__ = '8.1.2'
|
||||
__version__ = '8.3.0'
|
||||
|
||||
|
||||
def get_default_company(user=None):
|
||||
|
||||
@@ -62,13 +62,13 @@ class BankReconciliation(Document):
|
||||
|
||||
for d in entries:
|
||||
row = self.append('payment_entries', {})
|
||||
|
||||
d.amount = fmt_money(d.debit if d.debit else d.credit, 2, d.account_currency) + " " + (_("Dr") if d.debit else _("Cr"))
|
||||
amount = d.debit if d.debit else d.credit
|
||||
d.amount = fmt_money(amount, 2, d.account_currency) + " " + (_("Dr") if d.debit else _("Cr"))
|
||||
d.pop("credit")
|
||||
d.pop("debit")
|
||||
d.pop("account_currency")
|
||||
row.update(d)
|
||||
self.total_amount += flt(d.amount)
|
||||
self.total_amount += flt(amount)
|
||||
|
||||
def update_clearance_date(self):
|
||||
clearance_date_updated = False
|
||||
|
||||
@@ -733,11 +733,10 @@ def get_opening_accounts(company):
|
||||
accounts = frappe.db.sql_list("""select
|
||||
name from tabAccount
|
||||
where
|
||||
is_group=0 and
|
||||
report_type='Balance Sheet' and
|
||||
ifnull(warehouse, '') = '' and
|
||||
company=%s
|
||||
order by name asc""", company)
|
||||
is_group=0 and report_type='Balance Sheet' and company=%s and
|
||||
name not in(select distinct account from tabWarehouse where
|
||||
account is not null and account != '')
|
||||
order by name asc""", frappe.db.escape(company))
|
||||
|
||||
return [{"account": a, "balance": get_balance_on(a)} for a in accounts]
|
||||
|
||||
|
||||
@@ -224,9 +224,9 @@ cur_frm.fields_dict.cash_bank_account.get_query = function(doc) {
|
||||
return {
|
||||
filters: [
|
||||
["Account", "account_type", "in", ["Cash", "Bank"]],
|
||||
["Account", "root_type", "=", "Asset"],
|
||||
["Account", "is_group", "=",0],
|
||||
["Account", "company", "=", doc.company]
|
||||
["Account", "company", "=", doc.company],
|
||||
["Account", "report_type", "=", "Balance Sheet"]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3238,7 +3238,7 @@
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@@ -3767,10 +3767,11 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-06-13 14:28:57.930167",
|
||||
"modified": "2017-06-29 10:48:09.707735",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
"name_case": "Title Case",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
|
||||
@@ -71,17 +71,19 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
});
|
||||
|
||||
if(!from_delivery_note && !is_delivered_by_supplier) {
|
||||
cur_frm.add_custom_button(__('Delivery'), cur_frm.cscript['Make Delivery Note'],
|
||||
__("Make"));
|
||||
cur_frm.add_custom_button(__('Delivery'),
|
||||
cur_frm.cscript['Make Delivery Note'], __("Make"));
|
||||
}
|
||||
}
|
||||
|
||||
if(doc.outstanding_amount!=0 && !cint(doc.is_return)) {
|
||||
cur_frm.add_custom_button(__('Payment'), this.make_payment_entry, __("Make"));
|
||||
cur_frm.add_custom_button(__('Payment'),
|
||||
this.make_payment_entry, __("Make"));
|
||||
}
|
||||
|
||||
if(doc.outstanding_amount>0 && !cint(doc.is_return)) {
|
||||
cur_frm.add_custom_button(__('Payment Request'), this.make_payment_request, __("Make"));
|
||||
cur_frm.add_custom_button(__('Payment Request'),
|
||||
this.make_payment_request, __("Make"));
|
||||
}
|
||||
|
||||
|
||||
@@ -303,6 +305,23 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
}
|
||||
|
||||
this.frm.refresh_fields();
|
||||
},
|
||||
|
||||
company_address: function() {
|
||||
var me = this;
|
||||
if(this.frm.doc.company_address) {
|
||||
frappe.call({
|
||||
method: "frappe.contacts.doctype.address.address.get_address_display",
|
||||
args: {"address_dict": this.frm.doc.company_address },
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
me.frm.set_value("company_address_display", r.message)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.frm.set_value("company_address_display", "");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -324,11 +343,13 @@ cur_frm.cscript.hide_fields = function(doc) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var item_fields_stock = ['batch_no', 'actual_batch_qty', 'actual_qty', 'expense_account',
|
||||
'warehouse', 'expense_account', 'quality_inspection']
|
||||
cur_frm.fields_dict['items'].grid.set_column_disp(item_fields_stock,
|
||||
(cint(doc.update_stock)==1 || cint(doc.is_return)==1 ? true : false));
|
||||
|
||||
|
||||
// India related fields
|
||||
if (frappe.boot.sysdefaults.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']);
|
||||
else hide_field(['c_form_applicable', 'c_form_no']);
|
||||
@@ -481,7 +502,7 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
'Delivery Note': 'Delivery',
|
||||
'Sales Invoice': 'Sales Return',
|
||||
'Payment Request': 'Payment Request',
|
||||
'Payment': 'Payment Entry'
|
||||
'Payment Entry': 'Payment'
|
||||
},
|
||||
frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function(doc, cdt, cdn){
|
||||
return{
|
||||
@@ -502,8 +523,20 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
frm.set_query('company_address', function(doc) {
|
||||
if(!doc.company) {
|
||||
frappe.throw(_('Please set Company'));
|
||||
}
|
||||
|
||||
return {
|
||||
query: 'frappe.contacts.doctype.address.address.address_query',
|
||||
filters: {
|
||||
link_doctype: 'Company',
|
||||
link_name: doc.company
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
project: function(frm){
|
||||
|
||||
@@ -190,7 +190,7 @@
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
@@ -200,37 +200,6 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "due_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Due Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "due_date",
|
||||
"oldfieldtype": "Date",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@@ -253,7 +222,7 @@
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Project",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
@@ -314,7 +283,7 @@
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
@@ -345,7 +314,7 @@
|
||||
"options": "POS Profile",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
@@ -510,6 +479,37 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "due_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Due Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "due_date",
|
||||
"oldfieldtype": "Date",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@@ -844,6 +844,37 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "territory",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Territory",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Territory",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@@ -940,21 +971,21 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "",
|
||||
"fieldname": "customer_group",
|
||||
"fieldname": "company_address",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Customer Group",
|
||||
"label": "Company Address Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Customer Group",
|
||||
"options": "Address",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
@@ -971,24 +1002,23 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "territory",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"fieldname": "company_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"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": "Territory",
|
||||
"label": "Company Address",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Territory",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
@@ -1479,7 +1509,7 @@
|
||||
"options": "Sales Invoice Timesheet",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
@@ -1882,6 +1912,36 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fieldname": "sec_tax_breakup",
|
||||
"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": "Item-wise Tax Breakup",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@@ -1889,7 +1949,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "other_charges_calculation",
|
||||
"fieldtype": "HTML",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@@ -1899,12 +1959,12 @@
|
||||
"in_standard_filter": 0,
|
||||
"label": "Taxes and Charges Calculation",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"no_copy": 1,
|
||||
"oldfieldtype": "HTML",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
@@ -2511,7 +2571,7 @@
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Outstanding Amount",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
@@ -2984,7 +3044,7 @@
|
||||
"options": "Account",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
@@ -3489,6 +3549,37 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "",
|
||||
"fieldname": "customer_group",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Customer Group",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Customer Group",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@@ -3566,7 +3657,7 @@
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
@@ -4419,7 +4510,7 @@
|
||||
"options": "Print Format",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
@@ -4480,7 +4571,7 @@
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
@@ -4511,7 +4602,7 @@
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
@@ -4596,10 +4687,11 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-06-16 17:07:55.483734",
|
||||
"modified": "2017-07-04 17:11:09.477003",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
"name_case": "Title Case",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
|
||||
@@ -131,7 +131,8 @@ class SalesInvoice(SellingController):
|
||||
if not self.is_return:
|
||||
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
|
||||
self.check_credit_limit()
|
||||
self.update_serial_no()
|
||||
|
||||
self.update_serial_no()
|
||||
|
||||
if not cint(self.is_pos) == 1 and not self.is_return:
|
||||
self.update_against_document_in_jv()
|
||||
@@ -793,19 +794,18 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def update_serial_no(self, in_cancel=False):
|
||||
""" update Sales Invoice refrence in Serial No """
|
||||
invoice = None if (in_cancel or self.is_return) else self.name
|
||||
if in_cancel and self.is_return:
|
||||
invoice = self.return_against
|
||||
|
||||
for item in self.items:
|
||||
if not item.serial_no:
|
||||
continue
|
||||
|
||||
serial_nos = ["'%s'"%serial_no for serial_no in item.serial_no.split("\n")]
|
||||
|
||||
frappe.db.sql(""" update `tabSerial No` set sales_invoice='{invoice}'
|
||||
where name in ({serial_nos})""".format(
|
||||
invoice='' if in_cancel else self.name,
|
||||
serial_nos=",".join(serial_nos)
|
||||
)
|
||||
)
|
||||
for serial_no in item.serial_no.split("\n"):
|
||||
sno = frappe.get_doc('Serial No', serial_no)
|
||||
sno.sales_invoice = invoice
|
||||
sno.db_update()
|
||||
|
||||
def validate_serial_numbers(self):
|
||||
"""
|
||||
|
||||
@@ -1105,6 +1105,22 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
for i, k in enumerate(expected_values["keys"]):
|
||||
self.assertEquals(d.get(k), expected_values[d.item_code][i])
|
||||
|
||||
def test_item_wise_tax_breakup(self):
|
||||
si = create_sales_invoice(qty=100, rate=50, do_not_save=True)
|
||||
si.append("taxes", {
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account Service Tax - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Service Tax",
|
||||
"rate": 10
|
||||
})
|
||||
si.insert()
|
||||
|
||||
tax_breakup_html = '''\n<div class="tax-break-up" style="overflow-x: auto;">\n\t<table class="table table-bordered table-hover">\n\t\t<thead><tr><th class="text-left" style="min-width: 120px;">Item Name</th>\n<th class="text-right" style="min-width: 80px;">Taxable Amount</th>\n<th class="text-right" style="min-width: 80px;">_Test Account Service Tax - _TC</th></tr></thead>\n\t\t<tbody><tr><td>_Test Item</td><td class="text-right">\u20b9 5,000.00</td><td class="text-right">(10.0%) \u20b9 500.00</td></tr></tbody>\n\t</table>\n</div>'''
|
||||
|
||||
self.assertEqual(si.other_charges_calculation, tax_breakup_html)
|
||||
|
||||
|
||||
def create_sales_invoice(**args):
|
||||
si = frappe.new_doc("Sales Invoice")
|
||||
args = frappe._dict(args)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
@@ -2165,7 +2166,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-05-10 17:14:42.681757",
|
||||
"modified": "2017-07-06 17:54:03.347700",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
||||
@@ -7,11 +7,12 @@ import frappe
|
||||
import datetime
|
||||
from frappe import _, msgprint, scrub
|
||||
from frappe.defaults import get_user_permissions
|
||||
from frappe.model.utils import get_fetch_values
|
||||
from frappe.utils import add_days, getdate, formatdate, get_first_day, date_diff, \
|
||||
add_years, get_timestamp, nowdate, flt
|
||||
from frappe.contacts.doctype.address.address import get_address_display, get_default_address
|
||||
from frappe.contacts.doctype.contact.contact import get_contact_details, get_default_contact
|
||||
from erpnext.exceptions import PartyFrozen, InvalidCurrency, PartyDisabled, InvalidAccountCurrency
|
||||
from erpnext.exceptions import PartyFrozen, PartyDisabled, InvalidAccountCurrency
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from erpnext import get_default_currency
|
||||
|
||||
@@ -42,7 +43,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
|
||||
|
||||
party = frappe.get_doc(party_type, party)
|
||||
|
||||
set_address_details(out, party, party_type)
|
||||
set_address_details(out, party, party_type, doctype, company)
|
||||
set_contact_details(out, party, party_type)
|
||||
set_other_values(out, party, party_type)
|
||||
set_price_list(out, party, party_type, price_list)
|
||||
@@ -60,10 +61,11 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
|
||||
|
||||
return out
|
||||
|
||||
def set_address_details(out, party, party_type):
|
||||
def set_address_details(out, party, party_type, doctype=None, company=None):
|
||||
billing_address_field = "customer_address" if party_type == "Lead" \
|
||||
else party_type.lower() + "_address"
|
||||
out[billing_address_field] = get_default_address(party_type, party.name)
|
||||
out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field]))
|
||||
|
||||
# address display
|
||||
out.address_display = get_address_display(out[billing_address_field])
|
||||
@@ -72,6 +74,11 @@ def set_address_details(out, party, party_type):
|
||||
if party_type in ["Customer", "Lead"]:
|
||||
out.shipping_address_name = get_default_address(party_type, party.name, 'is_shipping_address')
|
||||
out.shipping_address = get_address_display(out["shipping_address_name"])
|
||||
out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name))
|
||||
|
||||
if doctype and doctype in ['Sales Invoice']:
|
||||
out.company_address = get_default_address('Company', company)
|
||||
out.update(get_fetch_values(doctype, 'company_address', out.company_address))
|
||||
|
||||
def set_contact_details(out, party, party_type):
|
||||
out.contact_person = get_default_contact(party_type, party.name)
|
||||
@@ -363,28 +370,28 @@ def get_timeline_data(doctype, name):
|
||||
out.update({ timestamp: count })
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def get_dashboard_info(party_type, party):
|
||||
current_fiscal_year = get_fiscal_year(nowdate(), as_dict=True)
|
||||
company = frappe.db.get_default("company") or frappe.get_all("Company")[0].name
|
||||
party_account_currency = get_party_account_currency(party_type, party, company)
|
||||
company_default_currency = get_default_currency() \
|
||||
or frappe.db.get_value('Company', company, 'default_currency')
|
||||
|
||||
|
||||
if party_account_currency==company_default_currency:
|
||||
total_field = "base_grand_total"
|
||||
else:
|
||||
total_field = "grand_total"
|
||||
|
||||
|
||||
doctype = "Sales Invoice" if party_type=="Customer" else "Purchase Invoice"
|
||||
|
||||
|
||||
billing_this_year = frappe.db.sql("""
|
||||
select sum({0})
|
||||
from `tab{1}`
|
||||
where {2}=%s and docstatus=1 and posting_date between %s and %s
|
||||
""".format(total_field, doctype, party_type.lower()),
|
||||
""".format(total_field, doctype, party_type.lower()),
|
||||
(party, current_fiscal_year.year_start_date, current_fiscal_year.year_end_date))
|
||||
|
||||
|
||||
total_unpaid = frappe.db.sql("""
|
||||
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
|
||||
from `tabGL Entry`
|
||||
@@ -396,5 +403,5 @@ def get_dashboard_info(party_type, party):
|
||||
info["total_unpaid"] = flt(total_unpaid[0][0]) if total_unpaid else 0
|
||||
if party_type == "Supplier":
|
||||
info["total_unpaid"] = -1 * info["total_unpaid"]
|
||||
|
||||
|
||||
return info
|
||||
|
||||
@@ -10,7 +10,7 @@ from frappe.utils import (flt, getdate, get_first_day, get_last_day, date_diff,
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
|
||||
|
||||
def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_values=False,
|
||||
def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_values=False,
|
||||
company=None, reset_period_on_fy_change=True):
|
||||
"""Get a list of dict {"from_date": from_date, "to_date": to_date, "key": key, "label": label}
|
||||
Periodicity can be (Yearly, Quarterly, Monthly)"""
|
||||
@@ -85,8 +85,8 @@ def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_v
|
||||
return period_list
|
||||
|
||||
def get_fiscal_year_data(from_fiscal_year, to_fiscal_year):
|
||||
fiscal_year = frappe.db.sql("""select min(year_start_date) as year_start_date,
|
||||
max(year_end_date) as year_end_date from `tabFiscal Year` where
|
||||
fiscal_year = frappe.db.sql("""select min(year_start_date) as year_start_date,
|
||||
max(year_end_date) as year_end_date from `tabFiscal Year` where
|
||||
name between %(from_fiscal_year)s and %(to_fiscal_year)s""",
|
||||
{'from_fiscal_year': from_fiscal_year, 'to_fiscal_year': to_fiscal_year}, as_dict=1)
|
||||
|
||||
@@ -110,7 +110,7 @@ def get_label(periodicity, from_date, to_date):
|
||||
label = formatdate(from_date, "MMM YY") + "-" + formatdate(to_date, "MMM YY")
|
||||
|
||||
return label
|
||||
|
||||
|
||||
def get_data(company, root_type, balance_must_be, period_list, filters=None,
|
||||
accumulated_values=1, only_current_fiscal_year=True, ignore_closing_entries=False,
|
||||
ignore_accumulated_values_for_fy=False):
|
||||
@@ -119,16 +119,16 @@ def get_data(company, root_type, balance_must_be, period_list, filters=None,
|
||||
return None
|
||||
|
||||
accounts, accounts_by_name, parent_children_map = filter_accounts(accounts)
|
||||
|
||||
|
||||
company_currency = frappe.db.get_value("Company", company, "default_currency")
|
||||
|
||||
gl_entries_by_account = {}
|
||||
for root in frappe.db.sql("""select lft, rgt from tabAccount
|
||||
where root_type=%s and ifnull(parent_account, '') = ''""", root_type, as_dict=1):
|
||||
|
||||
set_gl_entries_by_account(company,
|
||||
|
||||
set_gl_entries_by_account(company,
|
||||
period_list[0]["year_start_date"] if only_current_fiscal_year else None,
|
||||
period_list[-1]["to_date"],
|
||||
period_list[-1]["to_date"],
|
||||
root.lft, root.rgt, filters,
|
||||
gl_entries_by_account, ignore_closing_entries=ignore_closing_entries)
|
||||
|
||||
@@ -136,7 +136,7 @@ def get_data(company, root_type, balance_must_be, period_list, filters=None,
|
||||
accumulate_values_into_parents(accounts, accounts_by_name, period_list, accumulated_values)
|
||||
out = prepare_data(accounts, balance_must_be, period_list, company_currency)
|
||||
out = filter_out_zero_value_rows(out, parent_children_map)
|
||||
|
||||
|
||||
if out:
|
||||
add_total_row(out, root_type, balance_must_be, period_list, company_currency)
|
||||
|
||||
@@ -151,13 +151,13 @@ def calculate_values(accounts_by_name, gl_entries_by_account, period_list, accum
|
||||
|
||||
if entry.posting_date <= period.to_date:
|
||||
if (accumulated_values or entry.posting_date >= period.from_date) and \
|
||||
(not ignore_accumulated_values_for_fy or
|
||||
(not ignore_accumulated_values_for_fy or
|
||||
entry.fiscal_year == period.to_date_fiscal_year):
|
||||
d[period.key] = d.get(period.key, 0.0) + flt(entry.debit) - flt(entry.credit)
|
||||
|
||||
if entry.posting_date < period_list[0].year_start_date:
|
||||
d["opening_balance"] = d.get("opening_balance", 0.0) + flt(entry.debit) - flt(entry.credit)
|
||||
|
||||
|
||||
def accumulate_values_into_parents(accounts, accounts_by_name, period_list, accumulated_values):
|
||||
"""accumulate children's values in parent accounts"""
|
||||
for d in reversed(accounts):
|
||||
@@ -165,7 +165,7 @@ def accumulate_values_into_parents(accounts, accounts_by_name, period_list, accu
|
||||
for period in period_list:
|
||||
accounts_by_name[d.parent_account][period.key] = \
|
||||
accounts_by_name[d.parent_account].get(period.key, 0.0) + d.get(period.key, 0.0)
|
||||
|
||||
|
||||
accounts_by_name[d.parent_account]["opening_balance"] = \
|
||||
accounts_by_name[d.parent_account].get("opening_balance", 0.0) + d.get("opening_balance", 0.0)
|
||||
|
||||
@@ -173,15 +173,15 @@ def prepare_data(accounts, balance_must_be, period_list, company_currency):
|
||||
data = []
|
||||
year_start_date = period_list[0]["year_start_date"].strftime("%Y-%m-%d")
|
||||
year_end_date = period_list[-1]["year_end_date"].strftime("%Y-%m-%d")
|
||||
|
||||
|
||||
for d in accounts:
|
||||
# add to output
|
||||
has_value = False
|
||||
total = 0
|
||||
row = frappe._dict({
|
||||
"account_name": d.account_name,
|
||||
"account": d.name,
|
||||
"parent_account": d.parent_account,
|
||||
"account_name": _(d.account_name),
|
||||
"account": _(d.name),
|
||||
"parent_account": _(d.parent_account),
|
||||
"indent": flt(d.indent),
|
||||
"year_start_date": year_start_date,
|
||||
"year_end_date": year_end_date,
|
||||
@@ -192,7 +192,7 @@ def prepare_data(accounts, balance_must_be, period_list, company_currency):
|
||||
if d.get(period.key) and balance_must_be=="Credit":
|
||||
# change sign based on Debit or Credit, since calculation is done using (debit - credit)
|
||||
d[period.key] *= -1
|
||||
|
||||
|
||||
row[period.key] = flt(d.get(period.key, 0.0), 3)
|
||||
|
||||
if abs(row[period.key]) >= 0.005:
|
||||
@@ -203,9 +203,9 @@ def prepare_data(accounts, balance_must_be, period_list, company_currency):
|
||||
row["has_value"] = has_value
|
||||
row["total"] = total
|
||||
data.append(row)
|
||||
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def filter_out_zero_value_rows(data, parent_children_map, show_zero_values=False):
|
||||
data_with_value = []
|
||||
for d in data:
|
||||
@@ -224,8 +224,8 @@ def filter_out_zero_value_rows(data, parent_children_map, show_zero_values=False
|
||||
|
||||
def add_total_row(out, root_type, balance_must_be, period_list, company_currency):
|
||||
total_row = {
|
||||
"account_name": "'" + _("Total {0} ({1})").format(root_type, balance_must_be) + "'",
|
||||
"account": "'" + _("Total {0} ({1})").format(root_type, balance_must_be) + "'",
|
||||
"account_name": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'",
|
||||
"account": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'",
|
||||
"currency": company_currency
|
||||
}
|
||||
|
||||
@@ -235,11 +235,11 @@ def add_total_row(out, root_type, balance_must_be, period_list, company_currency
|
||||
total_row.setdefault(period.key, 0.0)
|
||||
total_row[period.key] += row.get(period.key, 0.0)
|
||||
row[period.key] = ""
|
||||
|
||||
|
||||
total_row.setdefault("total", 0.0)
|
||||
total_row["total"] += flt(row["total"])
|
||||
row["total"] = ""
|
||||
|
||||
|
||||
if total_row.has_key("total"):
|
||||
out.append(total_row)
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ frappe.query_reports["General Ledger"] = {
|
||||
}
|
||||
return party_type;
|
||||
},
|
||||
change: function() {
|
||||
on_change: function() {
|
||||
var party_type = frappe.query_report_filters_by_name.party_type.get_value();
|
||||
var party = frappe.query_report_filters_by_name.party.get_value();
|
||||
if(!party_type || !party) {
|
||||
|
||||
@@ -278,7 +278,7 @@ class GrossProfitGenerator(object):
|
||||
inner join `tabSales Invoice Item` on `tabSales Invoice Item`.parent = `tabSales Invoice`.name
|
||||
{sales_team_table}
|
||||
where
|
||||
`tabSales Invoice`.docstatus = 1 and `tabSales Invoice`.is_return != 1 {conditions} {match_cond}
|
||||
`tabSales Invoice`.docstatus = 1 {conditions} {match_cond}
|
||||
order by
|
||||
`tabSales Invoice`.posting_date desc, `tabSales Invoice`.posting_time desc"""
|
||||
.format(conditions=conditions, sales_person_cols=sales_person_cols,
|
||||
|
||||
@@ -7,11 +7,14 @@ from frappe import _
|
||||
from frappe.utils import flt
|
||||
|
||||
def execute(filters=None):
|
||||
return _execute(filters)
|
||||
|
||||
def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
|
||||
if not filters: filters = {}
|
||||
columns = get_columns()
|
||||
columns = get_columns(additional_table_columns)
|
||||
last_col = len(columns)
|
||||
|
||||
item_list = get_items(filters)
|
||||
item_list = get_items(filters, additional_query_columns)
|
||||
aii_account_map = get_aii_accounts()
|
||||
if item_list:
|
||||
item_row_tax, tax_accounts = get_tax_accounts(item_list, columns)
|
||||
@@ -23,7 +26,7 @@ def execute(filters=None):
|
||||
"width": 80
|
||||
})
|
||||
company_currency = frappe.db.get_value("Company", filters.company, "default_currency")
|
||||
|
||||
|
||||
data = []
|
||||
for d in item_list:
|
||||
purchase_receipt = None
|
||||
@@ -35,8 +38,16 @@ def execute(filters=None):
|
||||
|
||||
expense_account = d.expense_account or aii_account_map.get(d.company)
|
||||
row = [d.item_code, d.item_name, d.item_group, d.parent, d.posting_date, d.supplier,
|
||||
d.supplier_name, d.credit_to, d.mode_of_payment, d.project, d.company, d.purchase_order,
|
||||
purchase_receipt, expense_account, d.qty, d.base_net_rate, d.base_net_amount]
|
||||
d.supplier_name]
|
||||
|
||||
if additional_query_columns:
|
||||
for col in additional_query_columns:
|
||||
row.append(d.get(col))
|
||||
|
||||
row += [
|
||||
d.credit_to, d.mode_of_payment, d.project, d.company, d.purchase_order,
|
||||
purchase_receipt, expense_account, d.qty, d.base_net_rate, d.base_net_amount
|
||||
]
|
||||
|
||||
for tax in tax_accounts:
|
||||
row.append(item_row_tax.get(d.name, {}).get(tax, 0))
|
||||
@@ -49,17 +60,27 @@ def execute(filters=None):
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_columns():
|
||||
return [_("Item Code") + ":Link/Item:120", _("Item Name") + "::120",
|
||||
def get_columns(additional_table_columns):
|
||||
columns = [
|
||||
_("Item Code") + ":Link/Item:120", _("Item Name") + "::120",
|
||||
_("Item Group") + ":Link/Item Group:100", _("Invoice") + ":Link/Purchase Invoice:120",
|
||||
_("Posting Date") + ":Date:80", _("Supplier") + ":Link/Supplier:120",
|
||||
"Supplier Name::120", "Payable Account:Link/Account:120",
|
||||
"Supplier Name::120"
|
||||
]
|
||||
|
||||
if additional_table_columns:
|
||||
columns += additional_table_columns
|
||||
|
||||
columns += [
|
||||
"Payable Account:Link/Account:120",
|
||||
_("Mode of Payment") + ":Link/Mode of Payment:80", _("Project") + ":Link/Project:80",
|
||||
_("Company") + ":Link/Company:100", _("Purchase Order") + ":Link/Purchase Order:100",
|
||||
_("Purchase Receipt") + ":Link/Purchase Receipt:100", _("Expense Account") + ":Link/Account:140",
|
||||
_("Qty") + ":Float:120", _("Rate") + ":Currency/currency:120", _("Amount") + ":Currency/currency:120"
|
||||
]
|
||||
|
||||
return columns
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = ""
|
||||
|
||||
@@ -74,21 +95,23 @@ def get_conditions(filters):
|
||||
|
||||
return conditions
|
||||
|
||||
def get_items(filters):
|
||||
def get_items(filters, additional_query_columns):
|
||||
conditions = get_conditions(filters)
|
||||
match_conditions = frappe.build_match_conditions("Purchase Invoice")
|
||||
if additional_query_columns:
|
||||
additional_query_columns = ', ' + ', '.join(additional_query_columns)
|
||||
|
||||
return frappe.db.sql("""
|
||||
select
|
||||
select
|
||||
pi_item.name, pi_item.parent, pi.posting_date, pi.credit_to, pi.company,
|
||||
pi.supplier, pi.remarks, pi.base_net_total, pi_item.item_code, pi_item.item_name,
|
||||
pi_item.item_group, pi_item.project, pi_item.purchase_order, pi_item.purchase_receipt,
|
||||
pi_item.po_detail, pi_item.expense_account, pi_item.qty, pi_item.base_net_rate,
|
||||
pi_item.base_net_amount, pi.supplier_name, pi.mode_of_payment
|
||||
pi.supplier, pi.remarks, pi.base_net_total, pi_item.item_code, pi_item.item_name,
|
||||
pi_item.item_group, pi_item.project, pi_item.purchase_order, pi_item.purchase_receipt,
|
||||
pi_item.po_detail, pi_item.expense_account, pi_item.qty, pi_item.base_net_rate,
|
||||
pi_item.base_net_amount, pi.supplier_name, pi.mode_of_payment {0}
|
||||
from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item
|
||||
where pi.name = pi_item.parent and pi.docstatus = 1 %s %s
|
||||
order by pi.posting_date desc, pi_item.item_code desc
|
||||
""" % (conditions, match_conditions), filters, as_dict=1)
|
||||
""".format(additional_query_columns) % (conditions, match_conditions), filters, as_dict=1)
|
||||
|
||||
def get_aii_accounts():
|
||||
return dict(frappe.db.sql("select name, stock_received_but_not_billed from tabCompany"))
|
||||
@@ -104,11 +127,11 @@ def get_tax_accounts(item_list, columns):
|
||||
item_row_map.setdefault(d.parent, {}).setdefault(d.item_code, []).append(d)
|
||||
|
||||
tax_details = frappe.db.sql("""
|
||||
select
|
||||
select
|
||||
parent, account_head, item_wise_tax_detail, charge_type, base_tax_amount_after_discount_amount
|
||||
from `tabPurchase Taxes and Charges`
|
||||
where parenttype = 'Purchase Invoice' and docstatus = 1
|
||||
and (account_head is not null and account_head != '')
|
||||
from `tabPurchase Taxes and Charges`
|
||||
where parenttype = 'Purchase Invoice' and docstatus = 1
|
||||
and (account_head is not null and account_head != '')
|
||||
and category in ('Total', 'Valuation and Total')
|
||||
and parent in (%s)
|
||||
""" % ', '.join(['%s']*len(invoice_item_row)), tuple(invoice_item_row.keys()))
|
||||
@@ -120,17 +143,17 @@ def get_tax_accounts(item_list, columns):
|
||||
if item_wise_tax_detail:
|
||||
try:
|
||||
item_wise_tax_detail = json.loads(item_wise_tax_detail)
|
||||
|
||||
|
||||
for item_code, tax_amount in item_wise_tax_detail.items():
|
||||
tax_amount = flt(tax_amount[1]) if isinstance(tax_amount, list) else flt(tax_amount)
|
||||
|
||||
item_net_amount = sum([flt(d.base_net_amount)
|
||||
|
||||
item_net_amount = sum([flt(d.base_net_amount)
|
||||
for d in item_row_map.get(parent, {}).get(item_code, [])])
|
||||
|
||||
|
||||
for d in item_row_map.get(parent, {}).get(item_code, []):
|
||||
item_tax_amount = flt((tax_amount * d.base_net_amount) / item_net_amount) if item_net_amount else 0
|
||||
item_row_tax.setdefault(d.name, {})[account_head] = item_tax_amount
|
||||
|
||||
|
||||
except ValueError:
|
||||
continue
|
||||
elif charge_type == "Actual" and tax_amount:
|
||||
|
||||
@@ -8,11 +8,14 @@ from frappe.utils import flt
|
||||
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
|
||||
|
||||
def execute(filters=None):
|
||||
return _execute(filters)
|
||||
|
||||
def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
|
||||
if not filters: filters = {}
|
||||
columns = get_columns()
|
||||
columns = get_columns(additional_table_columns)
|
||||
last_col = len(columns)
|
||||
|
||||
item_list = get_items(filters)
|
||||
item_list = get_items(filters, additional_query_columns)
|
||||
if item_list:
|
||||
item_row_tax, tax_accounts = get_tax_accounts(item_list, columns)
|
||||
columns.append({
|
||||
@@ -21,7 +24,7 @@ def execute(filters=None):
|
||||
"fieldtype": "Data",
|
||||
"width": 80
|
||||
})
|
||||
company_currency = frappe.db.get_value("Company", filters.company, "default_currency")
|
||||
company_currency = frappe.db.get_value("Company", filters.get("company"), "default_currency")
|
||||
mode_of_payments = get_mode_of_payments(set([d.parent for d in item_list]))
|
||||
|
||||
data = []
|
||||
@@ -35,10 +38,17 @@ def execute(filters=None):
|
||||
if not delivery_note and d.update_stock:
|
||||
delivery_note = d.parent
|
||||
|
||||
row = [d.item_code, d.item_name, d.item_group, d.parent, d.posting_date, d.customer, d.customer_name,
|
||||
d.customer_group, d.debit_to, ", ".join(mode_of_payments.get(d.parent, [])),
|
||||
row = [d.item_code, d.item_name, d.item_group, d.parent, d.posting_date, d.customer, d.customer_name]
|
||||
|
||||
if additional_query_columns:
|
||||
for col in additional_query_columns:
|
||||
row.append(d.get(col))
|
||||
|
||||
row += [
|
||||
d.customer_group, d.debit_to, ", ".join(mode_of_payments.get(d.parent, [])),
|
||||
d.territory, d.project, d.company, d.sales_order,
|
||||
delivery_note, d.income_account, d.cost_center, d.qty, d.base_net_rate, d.base_net_amount]
|
||||
delivery_note, d.income_account, d.cost_center, d.qty, d.base_net_rate, d.base_net_amount
|
||||
]
|
||||
|
||||
for tax in tax_accounts:
|
||||
row.append(item_row_tax.get(d.name, {}).get(tax, 0))
|
||||
@@ -50,12 +60,18 @@ def execute(filters=None):
|
||||
|
||||
return columns, data
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
def get_columns(additional_table_columns):
|
||||
columns = [
|
||||
_("Item Code") + ":Link/Item:120", _("Item Name") + "::120",
|
||||
_("Item Group") + ":Link/Item Group:100", _("Invoice") + ":Link/Sales Invoice:120",
|
||||
_("Posting Date") + ":Date:80", _("Customer") + ":Link/Customer:120",
|
||||
_("Customer Name") + "::120", _("Customer Group") + ":Link/Customer Group:120",
|
||||
_("Customer Name") + "::120"]
|
||||
|
||||
if additional_table_columns:
|
||||
columns += additional_table_columns
|
||||
|
||||
columns += [
|
||||
_("Customer Group") + ":Link/Customer Group:120",
|
||||
_("Receivable Account") + ":Link/Account:120",
|
||||
_("Mode of Payment") + "::120", _("Territory") + ":Link/Territory:80",
|
||||
_("Project") + ":Link/Project:80", _("Company") + ":Link/Company:100",
|
||||
@@ -66,6 +82,8 @@ def get_columns():
|
||||
_("Amount") + ":Currency/currency:120"
|
||||
]
|
||||
|
||||
return columns
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = ""
|
||||
|
||||
@@ -76,15 +94,18 @@ def get_conditions(filters):
|
||||
("to_date", " and si.posting_date<=%(to_date)s")):
|
||||
if filters.get(opts[0]):
|
||||
conditions += opts[1]
|
||||
|
||||
|
||||
if filters.get("mode_of_payment"):
|
||||
conditions += """ and exists(select name from `tabSales Invoice Payment`
|
||||
where parent=si.name
|
||||
where parent=si.name
|
||||
and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)"""
|
||||
|
||||
return conditions
|
||||
|
||||
def get_items(filters):
|
||||
def get_items(filters, additional_query_columns):
|
||||
if additional_query_columns:
|
||||
additional_query_columns = ', ' + ', '.join(additional_query_columns)
|
||||
|
||||
conditions = get_conditions(filters)
|
||||
return frappe.db.sql("""
|
||||
select
|
||||
@@ -93,10 +114,11 @@ def get_items(filters):
|
||||
si_item.item_code, si_item.item_name, si_item.item_group, si_item.sales_order,
|
||||
si_item.delivery_note, si_item.income_account, si_item.cost_center, si_item.qty,
|
||||
si_item.base_net_rate, si_item.base_net_amount, si.customer_name,
|
||||
si.customer_group, si_item.so_detail, si.update_stock
|
||||
si.customer_group, si_item.so_detail, si.update_stock {0}
|
||||
from `tabSales Invoice` si, `tabSales Invoice Item` si_item
|
||||
where si.name = si_item.parent and si.docstatus = 1 %s
|
||||
order by si.posting_date desc, si_item.item_code desc""" % conditions, filters, as_dict=1)
|
||||
order by si.posting_date desc, si_item.item_code desc
|
||||
""".format(additional_query_columns or '') % conditions, filters, as_dict=1)
|
||||
|
||||
def get_tax_accounts(item_list, columns):
|
||||
import json
|
||||
|
||||
@@ -7,10 +7,13 @@ from frappe.utils import flt
|
||||
from frappe import msgprint, _
|
||||
|
||||
def execute(filters=None):
|
||||
return _execute(filters)
|
||||
|
||||
def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
|
||||
if not filters: filters = {}
|
||||
|
||||
invoice_list = get_invoices(filters)
|
||||
columns, expense_accounts, tax_accounts = get_columns(invoice_list)
|
||||
invoice_list = get_invoices(filters, additional_query_columns)
|
||||
columns, expense_accounts, tax_accounts = get_columns(invoice_list, additional_table_columns)
|
||||
|
||||
if not invoice_list:
|
||||
msgprint(_("No record found"))
|
||||
@@ -20,8 +23,9 @@ def execute(filters=None):
|
||||
invoice_expense_map, invoice_tax_map = get_invoice_tax_map(invoice_list,
|
||||
invoice_expense_map, expense_accounts)
|
||||
invoice_po_pr_map = get_invoice_po_pr_map(invoice_list)
|
||||
supplier_details = get_supplier_details(invoice_list)
|
||||
|
||||
suppliers = list(set([d.supplier for d in invoice_list]))
|
||||
supplier_details = get_supplier_details(suppliers)
|
||||
|
||||
company_currency = frappe.db.get_value("Company", filters.company, "default_currency")
|
||||
|
||||
data = []
|
||||
@@ -31,10 +35,18 @@ def execute(filters=None):
|
||||
purchase_receipt = list(set(invoice_po_pr_map.get(inv.name, {}).get("purchase_receipt", [])))
|
||||
project = list(set(invoice_po_pr_map.get(inv.name, {}).get("project", [])))
|
||||
|
||||
row = [inv.name, inv.posting_date, inv.supplier, inv.supplier_name,
|
||||
supplier_details.get(inv.supplier),
|
||||
inv.credit_to, inv.mode_of_payment, ", ".join(project), inv.bill_no, inv.bill_date, inv.remarks,
|
||||
", ".join(purchase_order), ", ".join(purchase_receipt), company_currency]
|
||||
row = [inv.name, inv.posting_date, inv.supplier, inv.supplier_name]
|
||||
|
||||
if additional_query_columns:
|
||||
for col in additional_query_columns:
|
||||
row.append(inv.get(col))
|
||||
|
||||
row += [
|
||||
supplier_details.get(inv.supplier), # supplier_type
|
||||
inv.credit_to, inv.mode_of_payment, ", ".join(project),
|
||||
inv.bill_no, inv.bill_date, inv.remarks,
|
||||
", ".join(purchase_order), ", ".join(purchase_receipt), company_currency
|
||||
]
|
||||
|
||||
# map expense values
|
||||
base_net_total = 0
|
||||
@@ -61,15 +73,20 @@ def execute(filters=None):
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_columns(invoice_list):
|
||||
def get_columns(invoice_list, additional_table_columns):
|
||||
"""return columns based on filters"""
|
||||
columns = [
|
||||
_("Invoice") + ":Link/Purchase Invoice:120", _("Posting Date") + ":Date:80",
|
||||
_("Supplier Id") + "::120", _("Supplier Name") + "::120",
|
||||
_("Supplier Type") + ":Link/Supplier Type:120", _("Payable Account") + ":Link/Account:120",
|
||||
_("Mode of Payment") + ":Link/Mode of Payment:80", _("Project") + ":Link/Project:80",
|
||||
_("Invoice") + ":Link/Purchase Invoice:120", _("Posting Date") + ":Date:80",
|
||||
_("Supplier Id") + "::120", _("Supplier Name") + "::120"]
|
||||
|
||||
if additional_table_columns:
|
||||
columns += additional_table_columns
|
||||
|
||||
columns += [
|
||||
_("Supplier Type") + ":Link/Supplier Type:120", _("Payable Account") + ":Link/Account:120",
|
||||
_("Mode of Payment") + ":Link/Mode of Payment:80", _("Project") + ":Link/Project:80",
|
||||
_("Bill No") + "::120", _("Bill Date") + ":Date:80", _("Remarks") + "::150",
|
||||
_("Purchase Order") + ":Link/Purchase Order:100",
|
||||
_("Purchase Order") + ":Link/Purchase Order:100",
|
||||
_("Purchase Receipt") + ":Link/Purchase Receipt:100",
|
||||
{
|
||||
"fieldname": "currency",
|
||||
@@ -114,27 +131,31 @@ def get_conditions(filters):
|
||||
|
||||
if filters.get("from_date"): conditions += " and posting_date>=%(from_date)s"
|
||||
if filters.get("to_date"): conditions += " and posting_date<=%(to_date)s"
|
||||
|
||||
|
||||
if filters.get("mode_of_payment"): conditions += " and ifnull(mode_of_payment, '') = %(mode_of_payment)s"
|
||||
|
||||
return conditions
|
||||
|
||||
def get_invoices(filters):
|
||||
def get_invoices(filters, additional_query_columns):
|
||||
if additional_query_columns:
|
||||
additional_query_columns = ', ' + ', '.join(additional_query_columns)
|
||||
|
||||
conditions = get_conditions(filters)
|
||||
return frappe.db.sql("""
|
||||
select
|
||||
name, posting_date, credit_to, supplier, supplier_name, bill_no, bill_date, remarks,
|
||||
base_net_total, base_grand_total, outstanding_amount, mode_of_payment
|
||||
from `tabPurchase Invoice`
|
||||
select
|
||||
name, posting_date, credit_to, supplier, supplier_name, bill_no, bill_date,
|
||||
remarks, base_net_total, base_grand_total, outstanding_amount,
|
||||
mode_of_payment {0}
|
||||
from `tabPurchase Invoice`
|
||||
where docstatus = 1 %s
|
||||
order by posting_date desc, name desc""" % conditions, filters, as_dict=1)
|
||||
order by posting_date desc, name desc""".format(additional_query_columns or '') % conditions, filters, as_dict=1)
|
||||
|
||||
|
||||
def get_invoice_expense_map(invoice_list):
|
||||
expense_details = frappe.db.sql("""
|
||||
select parent, expense_account, sum(base_net_amount) as amount
|
||||
from `tabPurchase Invoice Item`
|
||||
where parent in (%s)
|
||||
from `tabPurchase Invoice Item`
|
||||
where parent in (%s)
|
||||
group by parent, expense_account
|
||||
""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
|
||||
|
||||
@@ -149,7 +170,7 @@ def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts):
|
||||
tax_details = frappe.db.sql("""
|
||||
select parent, account_head, case add_deduct_tax when "Add" then sum(base_tax_amount_after_discount_amount)
|
||||
else sum(base_tax_amount_after_discount_amount) * -1 end as tax_amount
|
||||
from `tabPurchase Taxes and Charges`
|
||||
from `tabPurchase Taxes and Charges`
|
||||
where parent in (%s) and category in ('Total', 'Valuation and Total')
|
||||
group by parent, account_head, add_deduct_tax
|
||||
""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
|
||||
@@ -169,8 +190,8 @@ def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts):
|
||||
|
||||
def get_invoice_po_pr_map(invoice_list):
|
||||
pi_items = frappe.db.sql("""
|
||||
select parent, purchase_order, purchase_receipt, po_detail, project
|
||||
from `tabPurchase Invoice Item`
|
||||
select parent, purchase_order, purchase_receipt, po_detail, project
|
||||
from `tabPurchase Invoice Item`
|
||||
where parent in (%s) and (ifnull(purchase_order, '') != '' or ifnull(purchase_receipt, '') != '')
|
||||
""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
|
||||
|
||||
@@ -205,9 +226,8 @@ def get_account_details(invoice_list):
|
||||
|
||||
return account_map
|
||||
|
||||
def get_supplier_details(invoice_list):
|
||||
def get_supplier_details(suppliers):
|
||||
supplier_details = {}
|
||||
suppliers = list(set([inv.supplier for inv in invoice_list]))
|
||||
for supp in frappe.db.sql("""select name, supplier_type from `tabSupplier`
|
||||
where name in (%s)""" % ", ".join(["%s"]*len(suppliers)), tuple(suppliers), as_dict=1):
|
||||
supplier_details.setdefault(supp.name, supp.supplier_type)
|
||||
|
||||
@@ -7,10 +7,13 @@ from frappe.utils import flt
|
||||
from frappe import msgprint, _
|
||||
|
||||
def execute(filters=None):
|
||||
return _execute(filters)
|
||||
|
||||
def _execute(filters, additional_table_columns=None, additional_query_columns=None):
|
||||
if not filters: filters = frappe._dict({})
|
||||
|
||||
invoice_list = get_invoices(filters)
|
||||
columns, income_accounts, tax_accounts = get_columns(invoice_list)
|
||||
invoice_list = get_invoices(filters, additional_query_columns)
|
||||
columns, income_accounts, tax_accounts = get_columns(invoice_list, additional_table_columns)
|
||||
|
||||
if not invoice_list:
|
||||
msgprint(_("No record found"))
|
||||
@@ -21,8 +24,9 @@ def execute(filters=None):
|
||||
invoice_income_map, income_accounts)
|
||||
|
||||
invoice_so_dn_map = get_invoice_so_dn_map(invoice_list)
|
||||
customer_map = get_customer_details(invoice_list)
|
||||
company_currency = frappe.db.get_value("Company", filters.company, "default_currency")
|
||||
customers = list(set([inv.customer for inv in invoice_list]))
|
||||
customer_map = get_customer_details(customers)
|
||||
company_currency = frappe.db.get_value("Company", filters.get("company"), "default_currency")
|
||||
mode_of_payments = get_mode_of_payments([inv.name for inv in invoice_list])
|
||||
|
||||
data = []
|
||||
@@ -31,12 +35,22 @@ def execute(filters=None):
|
||||
sales_order = list(set(invoice_so_dn_map.get(inv.name, {}).get("sales_order", [])))
|
||||
delivery_note = list(set(invoice_so_dn_map.get(inv.name, {}).get("delivery_note", [])))
|
||||
|
||||
row = [inv.name, inv.posting_date, inv.customer, inv.customer_name,
|
||||
customer_map.get(inv.customer, {}).get("customer_group"),
|
||||
customer_map.get(inv.customer, {}).get("territory"),
|
||||
inv.debit_to, ", ".join(mode_of_payments.get(inv.name, [])), inv.project, inv.remarks,
|
||||
", ".join(sales_order), ", ".join(delivery_note), company_currency]
|
||||
customer_details = customer_map.get(inv.customer, {})
|
||||
row = [
|
||||
inv.name, inv.posting_date, inv.customer, inv.customer_name
|
||||
]
|
||||
|
||||
if additional_query_columns:
|
||||
for col in additional_query_columns:
|
||||
row.append(inv.get(col))
|
||||
|
||||
row +=[
|
||||
customer_details.get("customer_group"),
|
||||
customer_details.get("territory"),
|
||||
inv.debit_to, ", ".join(mode_of_payments.get(inv.name, [])),
|
||||
inv.project, inv.remarks,
|
||||
", ".join(sales_order), ", ".join(delivery_note), company_currency
|
||||
]
|
||||
# map income values
|
||||
base_net_total = 0
|
||||
for income_acc in income_accounts:
|
||||
@@ -62,15 +76,20 @@ def execute(filters=None):
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_columns(invoice_list):
|
||||
def get_columns(invoice_list, additional_table_columns):
|
||||
"""return columns based on filters"""
|
||||
columns = [
|
||||
_("Invoice") + ":Link/Sales Invoice:120", _("Posting Date") + ":Date:80",
|
||||
_("Customer Id") + "::120", _("Customer Name") + "::120",
|
||||
_("Customer Group") + ":Link/Customer Group:120", _("Territory") + ":Link/Territory:80",
|
||||
_("Receivable Account") + ":Link/Account:120", _("Mode of Payment") + "::120",
|
||||
_("Project") +":Link/Project:80", _("Remarks") + "::150",
|
||||
_("Invoice") + ":Link/Sales Invoice:120", _("Posting Date") + ":Date:80",
|
||||
_("Customer") + ":Link/Customer:120", _("Customer Name") + "::120"
|
||||
]
|
||||
|
||||
if additional_table_columns:
|
||||
columns += additional_table_columns
|
||||
|
||||
columns +=[
|
||||
_("Customer Group") + ":Link/Customer Group:120", _("Territory") + ":Link/Territory:80",
|
||||
_("Receivable Account") + ":Link/Account:120", _("Mode of Payment") + "::120",
|
||||
_("Project") +":Link/Project:80", _("Remarks") + "::150",
|
||||
_("Sales Order") + ":Link/Sales Order:100", _("Delivery Note") + ":Link/Delivery Note:100",
|
||||
{
|
||||
"fieldname": "currency",
|
||||
@@ -113,20 +132,23 @@ def get_conditions(filters):
|
||||
|
||||
if filters.get("from_date"): conditions += " and posting_date >= %(from_date)s"
|
||||
if filters.get("to_date"): conditions += " and posting_date <= %(to_date)s"
|
||||
|
||||
|
||||
if filters.get("mode_of_payment"):
|
||||
conditions += """ and exists(select name from `tabSales Invoice Payment`
|
||||
where parent=`tabSales Invoice`.name
|
||||
where parent=`tabSales Invoice`.name
|
||||
and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)"""
|
||||
|
||||
|
||||
return conditions
|
||||
|
||||
def get_invoices(filters):
|
||||
def get_invoices(filters, additional_query_columns):
|
||||
if additional_query_columns:
|
||||
additional_query_columns = ', ' + ', '.join(additional_query_columns)
|
||||
|
||||
conditions = get_conditions(filters)
|
||||
return frappe.db.sql("""select name, posting_date, debit_to, project, customer, customer_name, remarks,
|
||||
base_net_total, base_grand_total, base_rounded_total, outstanding_amount
|
||||
return frappe.db.sql("""select name, posting_date, debit_to, project, customer, customer_name, remarks,
|
||||
base_net_total, base_grand_total, base_rounded_total, outstanding_amount {0}
|
||||
from `tabSales Invoice`
|
||||
where docstatus = 1 %s order by posting_date desc, name desc""" %
|
||||
where docstatus = 1 %s order by posting_date desc, name desc""".format(additional_query_columns or '') %
|
||||
conditions, filters, as_dict=1)
|
||||
|
||||
def get_invoice_income_map(invoice_list):
|
||||
@@ -184,9 +206,8 @@ def get_invoice_so_dn_map(invoice_list):
|
||||
|
||||
return invoice_so_dn_map
|
||||
|
||||
def get_customer_details(invoice_list):
|
||||
def get_customer_details(customers):
|
||||
customer_map = {}
|
||||
customers = list(set([inv.customer for inv in invoice_list]))
|
||||
for cust in frappe.db.sql("""select name, territory, customer_group from `tabCustomer`
|
||||
where name in (%s)""" % ", ".join(["%s"]*len(customers)), tuple(customers), as_dict=1):
|
||||
customer_map.setdefault(cust.name, cust)
|
||||
|
||||
@@ -270,6 +270,9 @@ def make_purchase_receipt(source_name, target_doc=None):
|
||||
doc = get_mapped_doc("Purchase Order", source_name, {
|
||||
"Purchase Order": {
|
||||
"doctype": "Purchase Receipt",
|
||||
"field_map": {
|
||||
"per_billed": "per_billed"
|
||||
},
|
||||
"validation": {
|
||||
"docstatus": ["=", 1],
|
||||
}
|
||||
@@ -311,6 +314,9 @@ def make_purchase_invoice(source_name, target_doc=None):
|
||||
doc = get_mapped_doc("Purchase Order", source_name, {
|
||||
"Purchase Order": {
|
||||
"doctype": "Purchase Invoice",
|
||||
"field_map": {
|
||||
"party_account_currency": "party_account_currency"
|
||||
},
|
||||
"validation": {
|
||||
"docstatus": ["=", 1],
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from erpnext.templates.pages.rfq import check_supplier_has_docname_access
|
||||
from frappe.utils import nowdate
|
||||
|
||||
class TestRequestforQuotation(unittest.TestCase):
|
||||
@@ -28,6 +30,31 @@ class TestRequestforQuotation(unittest.TestCase):
|
||||
self.assertEquals(sq1.get('items')[0].item_code, "_Test Item")
|
||||
self.assertEquals(sq1.get('items')[0].qty, 5)
|
||||
|
||||
def test_make_supplier_quotation_with_special_characters(self):
|
||||
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation
|
||||
|
||||
frappe.delete_doc_if_exists("Supplier", "_Test Supplier '1", force=1)
|
||||
supplier = frappe.new_doc("Supplier")
|
||||
supplier.supplier_name = "_Test Supplier '1"
|
||||
supplier.supplier_type = "_Test Supplier Type"
|
||||
supplier.insert()
|
||||
|
||||
rfq = make_request_for_quotation(supplier_wt_appos)
|
||||
|
||||
sq = make_supplier_quotation(rfq.name, supplier_wt_appos[0].get("supplier"))
|
||||
sq.submit()
|
||||
|
||||
frappe.form_dict = frappe.local("form_dict")
|
||||
frappe.form_dict.name = rfq.name
|
||||
|
||||
self.assertEqual(
|
||||
check_supplier_has_docname_access(supplier_wt_appos[0].get('supplier')),
|
||||
True
|
||||
)
|
||||
|
||||
# reset form_dict
|
||||
frappe.form_dict.name = None
|
||||
|
||||
def test_make_supplier_quotation_from_portal(self):
|
||||
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation
|
||||
rfq = make_request_for_quotation()
|
||||
@@ -44,8 +71,11 @@ class TestRequestforQuotation(unittest.TestCase):
|
||||
self.assertEquals(supplier_quotation_doc.get('items')[0].amount, 500)
|
||||
|
||||
|
||||
def make_request_for_quotation():
|
||||
supplier_data = get_supplier_data()
|
||||
def make_request_for_quotation(supplier_data=None):
|
||||
"""
|
||||
:param supplier_data: List containing supplier data
|
||||
"""
|
||||
supplier_data = supplier_data if supplier_data else get_supplier_data()
|
||||
rfq = frappe.new_doc('Request for Quotation')
|
||||
rfq.transaction_date = nowdate()
|
||||
rfq.status = 'Draft'
|
||||
@@ -77,3 +107,8 @@ def get_supplier_data():
|
||||
"supplier": "_Test Supplier 1",
|
||||
"supplier_name": "_Test Supplier 1"
|
||||
}]
|
||||
|
||||
supplier_wt_appos = [{
|
||||
"supplier": "_Test Supplier '1",
|
||||
"supplier_name": "_Test Supplier '1",
|
||||
}]
|
||||
|
||||
3
erpnext/buying/doctype/supplier/regional/india.js
Normal file
@@ -0,0 +1,3 @@
|
||||
{% include "erpnext/regional/india/party.js" %}
|
||||
|
||||
erpnext.setup_gst_reminder_button('Supplier');
|
||||
@@ -1,3 +1,3 @@
|
||||
frappe.listview_settings['Supplier'] = {
|
||||
add_fields: ["supplier_name", "supplier_type"],
|
||||
add_fields: ["supplier_name", "supplier_type", "image"],
|
||||
};
|
||||
|
||||
8
erpnext/change_log/v8/v8_3_0.md
Normal file
@@ -0,0 +1,8 @@
|
||||
### Production Order Enahancement
|
||||
- Show required items child table.
|
||||
- Source warehouse for each Raw Materials, in Production Order Item and BOM Item table.
|
||||
- Group warehouse allowed for Source and WIP warehouse.
|
||||
### GST Tax Invoice Print Format
|
||||
- Added print format to show tax(GST) breakup.
|
||||
- Total Stock Summary report.
|
||||
- Include Search Fields in Customer Query.
|
||||
@@ -35,7 +35,7 @@ def get_data():
|
||||
{
|
||||
"type": "report",
|
||||
"name": "Accounts Receivable",
|
||||
"doctype": "Sales Invoice",
|
||||
"doctype": "Sales Invoice",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
@@ -198,6 +198,39 @@ def get_data():
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": _("Goods and Services Tax (GST India)"),
|
||||
"items": [
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "GST Settings",
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "GST HSN Code",
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GST Sales Register",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GST Purchase Register",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GST Itemised Sales Register",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GST Itemised Purchase Register",
|
||||
"is_query_report": True
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": _("Budget and Cost Center"),
|
||||
"items": [
|
||||
|
||||
@@ -137,7 +137,14 @@ def get_data():
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Assessment Result Tool"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Course wise Assessment Report",
|
||||
"doctype": "Assessment Result"
|
||||
},
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -49,6 +49,12 @@ def get_data():
|
||||
"doctype": "Issue",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "Support Hours",
|
||||
"doctype": "Issue",
|
||||
"is_query_report": True
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
@@ -255,7 +255,7 @@ class BuyingController(StockController):
|
||||
|
||||
def get_items_from_bom(self, item_code, bom):
|
||||
bom_items = frappe.db.sql("""select t2.item_code,
|
||||
t2.qty / ifnull(t1.quantity, 1) as qty_consumed_per_unit,
|
||||
t2.stock_qty / ifnull(t1.quantity, 1) as qty_consumed_per_unit,
|
||||
t2.rate, t2.stock_uom, t2.name, t2.description
|
||||
from `tabBOM` t1, `tabBOM Item` t2, tabItem t3
|
||||
where t2.parent = t1.name and t1.item = %s
|
||||
|
||||
@@ -169,6 +169,7 @@ def create_variant(item, args):
|
||||
|
||||
return variant
|
||||
|
||||
|
||||
def copy_attributes_to_variant(item, variant):
|
||||
from frappe.model import no_value_fields
|
||||
|
||||
@@ -181,8 +182,9 @@ def copy_attributes_to_variant(item, variant):
|
||||
exclude_fields += ['manufacturer', 'manufacturer_part_no']
|
||||
|
||||
for field in item.meta.fields:
|
||||
if field.fieldtype not in no_value_fields and (not field.no_copy)\
|
||||
and field.fieldname not in exclude_fields:
|
||||
# "Table" is part of `no_value_field` but we shouldn't ignore tables
|
||||
if (field.fieldtype == 'Table' or field.fieldtype not in no_value_fields) \
|
||||
and (not field.no_copy) and field.fieldname not in exclude_fields:
|
||||
if variant.get(field.fieldname) != item.get(field.fieldname):
|
||||
variant.set(field.fieldname, item.get(field.fieldname))
|
||||
variant.variant_of = item.name
|
||||
|
||||
@@ -68,14 +68,17 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
fields = ["name", "customer_name", "customer_group", "territory"]
|
||||
|
||||
meta = frappe.get_meta("Customer")
|
||||
fields = fields + [f for f in meta.get_search_fields() if not f in fields]
|
||||
searchfields = meta.get_search_fields()
|
||||
searchfields = searchfields + [f for f in [searchfield or "name", "customer_name"] \
|
||||
if not f in searchfields]
|
||||
fields = fields + [f for f in searchfields if not f in fields]
|
||||
|
||||
fields = ", ".join(fields)
|
||||
searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
|
||||
|
||||
return frappe.db.sql("""select {fields} from `tabCustomer`
|
||||
where docstatus < 2
|
||||
and ({key} like %(txt)s
|
||||
or customer_name like %(txt)s) and disabled=0
|
||||
and ({scond}) and disabled=0
|
||||
{mcond}
|
||||
order by
|
||||
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
|
||||
@@ -84,7 +87,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
name, customer_name
|
||||
limit %(start)s, %(page_len)s""".format(**{
|
||||
"fields": fields,
|
||||
"key": searchfield,
|
||||
"scond": searchfields,
|
||||
"mcond": get_match_cond(doctype)
|
||||
}), {
|
||||
'txt': "%%%s%%" % txt,
|
||||
|
||||
@@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
||||
import json
|
||||
import frappe, erpnext
|
||||
from frappe import _, scrub
|
||||
from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
|
||||
from frappe.utils import cint, flt, cstr, fmt_money, round_based_on_smallest_currency_fraction
|
||||
from erpnext.controllers.accounts_controller import validate_conversion_rate, \
|
||||
validate_taxes_and_charges, validate_inclusive_tax
|
||||
|
||||
@@ -24,6 +24,9 @@ class calculate_taxes_and_totals(object):
|
||||
|
||||
if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
self.calculate_total_advance()
|
||||
|
||||
if self.doc.meta.get_field("other_charges_calculation"):
|
||||
self.set_item_wise_tax_breakup()
|
||||
|
||||
def _calculate(self):
|
||||
self.calculate_item_values()
|
||||
@@ -504,3 +507,105 @@ class calculate_taxes_and_totals(object):
|
||||
rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
|
||||
|
||||
return rate_with_margin
|
||||
|
||||
def set_item_wise_tax_breakup(self):
|
||||
item_tax = {}
|
||||
tax_accounts = []
|
||||
company_currency = erpnext.get_company_currency(self.doc.company)
|
||||
|
||||
item_tax, tax_accounts = self.get_item_tax(item_tax, tax_accounts, company_currency)
|
||||
|
||||
headings = get_table_column_headings(tax_accounts)
|
||||
|
||||
distinct_items = self.get_distinct_items()
|
||||
|
||||
rows = get_table_rows(distinct_items, item_tax, tax_accounts, company_currency)
|
||||
|
||||
if not rows:
|
||||
self.doc.other_charges_calculation = ""
|
||||
else:
|
||||
self.doc.other_charges_calculation = '''
|
||||
<div class="tax-break-up" style="overflow-x: auto;">
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead><tr>{headings}</tr></thead>
|
||||
<tbody>{rows}</tbody>
|
||||
</table>
|
||||
</div>'''.format(**{
|
||||
"headings": "\n".join(headings),
|
||||
"rows": "\n".join(rows)
|
||||
})
|
||||
|
||||
def get_item_tax(self, item_tax, tax_accounts, company_currency):
|
||||
for tax in self.doc.taxes:
|
||||
tax_amount_precision = tax.precision("tax_amount")
|
||||
tax_rate_precision = tax.precision("rate");
|
||||
|
||||
item_tax_map = self._load_item_tax_rate(tax.item_wise_tax_detail)
|
||||
for item_code, tax_data in item_tax_map.items():
|
||||
if not item_tax.get(item_code):
|
||||
item_tax[item_code] = {}
|
||||
|
||||
if isinstance(tax_data, list):
|
||||
tax_rate = ""
|
||||
if tax_data[0]:
|
||||
if tax.charge_type == "Actual":
|
||||
tax_rate = fmt_money(flt(tax_data[0], tax_amount_precision),
|
||||
tax_amount_precision, company_currency)
|
||||
else:
|
||||
tax_rate = cstr(flt(tax_data[0], tax_rate_precision)) + "%"
|
||||
|
||||
tax_amount = fmt_money(flt(tax_data[1], tax_amount_precision),
|
||||
tax_amount_precision, company_currency)
|
||||
|
||||
item_tax[item_code][tax.name] = [tax_rate, tax_amount]
|
||||
else:
|
||||
item_tax[item_code][tax.name] = [cstr(flt(tax_data, tax_rate_precision)) + "%", ""]
|
||||
tax_accounts.append([tax.name, tax.account_head])
|
||||
|
||||
return item_tax, tax_accounts
|
||||
|
||||
|
||||
def get_distinct_items(self):
|
||||
distinct_item_names = []
|
||||
distinct_items = []
|
||||
for item in self.doc.items:
|
||||
item_code = item.item_code or item.item_name
|
||||
if item_code not in distinct_item_names:
|
||||
distinct_item_names.append(item_code)
|
||||
distinct_items.append(item)
|
||||
|
||||
return distinct_items
|
||||
|
||||
def get_table_column_headings(tax_accounts):
|
||||
headings_name = [_("Item Name"), _("Taxable Amount")] + [d[1] for d in tax_accounts]
|
||||
headings = []
|
||||
for head in headings_name:
|
||||
if head == _("Item Name"):
|
||||
headings.append('<th style="min-width: 120px;" class="text-left">' + (head or "") + "</th>")
|
||||
else:
|
||||
headings.append('<th style="min-width: 80px;" class="text-right">' + (head or "") + "</th>")
|
||||
|
||||
return headings
|
||||
|
||||
def get_table_rows(distinct_items, item_tax, tax_accounts, company_currency):
|
||||
rows = []
|
||||
for item in distinct_items:
|
||||
item_tax_record = item_tax.get(item.item_code or item.item_name)
|
||||
if not item_tax_record:
|
||||
continue
|
||||
|
||||
taxes = []
|
||||
for head in tax_accounts:
|
||||
if item_tax_record[head[0]]:
|
||||
taxes.append("<td class='text-right'>(" + item_tax_record[head[0]][0] + ") "
|
||||
+ item_tax_record[head[0]][1] + "</td>")
|
||||
else:
|
||||
taxes.append("<td></td>")
|
||||
|
||||
rows.append("<tr><td>{item_name}</td><td class='text-right'>{taxable_amount}</td>{taxes}</tr>".format(**{
|
||||
"item_name": item.item_name,
|
||||
"taxable_amount": fmt_money(item.net_amount, item.precision("net_amount"), company_currency),
|
||||
"taxes": "\n".join(taxes)
|
||||
}))
|
||||
|
||||
return rows
|
||||
58
erpnext/controllers/tests/test_item_variant.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import json
|
||||
import unittest
|
||||
|
||||
from erpnext.controllers.item_variant import copy_attributes_to_variant, make_variant_item_code
|
||||
|
||||
# python 3 compatibility stuff
|
||||
try:
|
||||
unicode = unicode
|
||||
except NameError:
|
||||
# Python 3
|
||||
basestring = (str, bytes)
|
||||
else:
|
||||
# Python 2
|
||||
basestring = basestring
|
||||
|
||||
|
||||
def create_variant_with_tables(item, args):
|
||||
if isinstance(args, basestring):
|
||||
args = json.loads(args)
|
||||
|
||||
template = frappe.get_doc("Item", item)
|
||||
template.quality_parameters.append({
|
||||
"specification": "Moisture",
|
||||
"value": "< 5%",
|
||||
})
|
||||
variant = frappe.new_doc("Item")
|
||||
variant.variant_based_on = 'Item Attribute'
|
||||
variant_attributes = []
|
||||
|
||||
for d in template.attributes:
|
||||
variant_attributes.append({
|
||||
"attribute": d.attribute,
|
||||
"attribute_value": args.get(d.attribute)
|
||||
})
|
||||
|
||||
variant.set("attributes", variant_attributes)
|
||||
copy_attributes_to_variant(template, variant)
|
||||
make_variant_item_code(template.item_code, template.item_name, variant)
|
||||
|
||||
return variant
|
||||
|
||||
|
||||
def make_item_variant():
|
||||
frappe.delete_doc_if_exists("Item", "_Test Variant Item-S", force=1)
|
||||
variant = create_variant_with_tables("_Test Variant Item", '{"Test Size": "Small"}')
|
||||
variant.item_code = "_Test Variant Item-S"
|
||||
variant.item_name = "_Test Variant Item-S"
|
||||
variant.save()
|
||||
return variant
|
||||
|
||||
|
||||
class TestItemVariant(unittest.TestCase):
|
||||
def test_tables_in_template_copied_to_variant(self):
|
||||
variant = make_item_variant()
|
||||
self.assertNotEqual(variant.get("quality_parameters"), [])
|
||||
@@ -316,6 +316,8 @@ def setup_account():
|
||||
doc.parent_account = frappe.db.get_value('Account', {'account_name': doc.parent_account})
|
||||
doc.insert()
|
||||
|
||||
frappe.flags.in_import = False
|
||||
|
||||
def setup_account_to_expense_type():
|
||||
company_abbr = frappe.db.get_value("Company", erpnext.get_default_company(), "abbr")
|
||||
expense_types = [{'name': _('Calls'), "account": "Sales Expenses - "+ company_abbr},
|
||||
@@ -380,4 +382,6 @@ def import_json(doctype, submit=False, values=None):
|
||||
|
||||
frappe.db.commit()
|
||||
|
||||
frappe.flags.in_import = False
|
||||
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 261 KiB |
0
erpnext/docs/assets/img/regional/__init__.py
Normal file
0
erpnext/docs/assets/img/regional/india/__init__.py
Normal file
|
After Width: | Height: | Size: 215 KiB |
BIN
erpnext/docs/assets/img/regional/india/gst-in-coa.png
Normal file
|
After Width: | Height: | Size: 279 KiB |
BIN
erpnext/docs/assets/img/regional/india/gst-invoice.gif
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
erpnext/docs/assets/img/regional/india/gst-itemised.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
erpnext/docs/assets/img/regional/india/gst-menu.png
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
erpnext/docs/assets/img/regional/india/gst-settings.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
erpnext/docs/assets/img/regional/india/gst-template-in-state.png
Normal file
|
After Width: | Height: | Size: 162 KiB |
BIN
erpnext/docs/assets/img/regional/india/gstin-company.gif
Normal file
|
After Width: | Height: | Size: 538 KiB |
BIN
erpnext/docs/assets/img/regional/india/gstin-customer.gif
Normal file
|
After Width: | Height: | Size: 789 KiB |
BIN
erpnext/docs/assets/img/regional/india/gstin-portal-update.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
erpnext/docs/assets/img/regional/india/gstin-reminder-email.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
erpnext/docs/assets/img/regional/india/hsn-item.gif
Normal file
|
After Width: | Height: | Size: 337 KiB |
|
After Width: | Height: | Size: 185 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
BIN
erpnext/docs/assets/img/setup/stock-reco-data.png
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
erpnext/docs/assets/img/setup/stock-reco-ledger.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
erpnext/docs/assets/img/setup/stock-reco-upload.gif
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 291 KiB After Width: | Height: | Size: 228 KiB |
|
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 199 KiB |
|
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
BIN
erpnext/docs/assets/img/stock/batch_no_modal.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 78 KiB |
BIN
erpnext/docs/assets/img/stock/serial_no_modal.gif
Normal file
|
After Width: | Height: | Size: 3.6 MiB |
BIN
erpnext/docs/assets/img/support/support_hours.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 28 KiB |
@@ -640,8 +640,8 @@ attach them to the start of each source file to most effectively state
|
||||
the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.</p>
|
||||
|
||||
<pre><code> <one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
<pre><code> <one line="" to="" give="" the="" program's="" name="" and="" a="" brief="" idea="" of="" what="" it="" does.="">
|
||||
Copyright (C) <year> <name of="" author="">
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -38,15 +38,13 @@ Hinweis: Stellen Sie sicher, dass im Abschnitt "Weitere Informationen" "Ist Erö
|
||||
|
||||
Vervollständigen Sie die Buchungssätze auf der Soll- und Haben-Seite.
|
||||
|
||||

|
||||
<img class="screenshot" alt="Opening Account" src="{{docs_base_url}}/assets/img/accounts/opening-6.png">
|
||||
|
||||
Um einen Eröffnungsstand einzupflegen, erstellen Sie einen Buchungssatz für ein Konto oder eine Gruppe von Konten.
|
||||
|
||||
Beispiel: Wenn Sie die Kontenstände von drei Bankkonten einpflegen möchten, dann erstellen Sie Buchungssätze der folgenden Art und Weise:
|
||||
|
||||

|
||||
|
||||

|
||||
<img class="screenshot" alt="Opening Account" src="{{docs_base_url}}/assets/img/accounts/opening-3.png">
|
||||
|
||||
Um einen Ausgleich herzustellen, wird ein temporäres Konto für Vermögen und Verbindlichkeiten verwendet. Wenn Sie einen Anfangsbestand in einem Verbindlichkeitenkonto einpflegen, können Sie zum Ausgleich ein temporäres Vermögenskonto verwenden.
|
||||
|
||||
@@ -61,7 +59,8 @@ Sie können zwei Eröffnungsbuchungssätze erstellen:
|
||||
|
||||
Wenn Sie die Buchungen erstellt haben, schaut der Bericht zur Probebilanz in etwa wie folgt aus:
|
||||
|
||||

|
||||
<img class="screenshot" alt="Probebilanz" src="{{docs_base_url}}/assets/img/accounts/opening-4.png">
|
||||
|
||||
|
||||
### Offene Rechnungen
|
||||
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
|
||||
Dieses Diagramm stellt dar, wie ERPNext die Informationen und Vorgänge in Ihrem Unternehmen über Schlüsselfunktionen nachverfolgt. Dieses Diagramm gibt nicht alle Funktionalitäten von ERPNext wieder.
|
||||
|
||||

|
||||
|
||||
<img class="screenshot" alt="Hohe Auflösung" src="{{docs_base_url}}/assets/img/setup/overview.png">
|
||||
|
||||
[Hohe Auflösung]({{docs_base_url}}/assets/old_images/erpnext/overview.png)
|
||||
|
||||
_Anmerkung: Nicht alle Schritte sind zwingend erforderlich. ERPNext erlaubt es Ihnen nach eigenem Gutdünken Schritte auszulassen, wenn Sie den Prozess vereinfachen wollen._
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ Wenn Sie bei Ihrer Tätigkeit bestimmte Prozesse an eine Drittpartei, bei der Si
|
||||
2. Erstellen Sie ein Lager für den Lieferanten, damit Sie die übergebenen Artikel nachverfolgen können (möglicherweise geben Sie ja Artikel im Wert einer Monatslieferung außer Haus).
|
||||
3. Stellen Sie für den bearbeiteten Artikel und der Artikelvorlage den Punkt "Ist Fremdvergabe" auf JA ein.
|
||||
|
||||

|
||||
<img class="screenshot" alt="Fremdvergabe" src="{{docs_base_url}}/assets/img/manufacturing/subcontract.png">
|
||||
|
||||
|
||||
**Schritt 1:** Erstellen Sie für den bearbeiteten Artikel eine Stückliste, die den unbearbeiteten Artikel als Unterartikel enthält. Beispiel: Wenn Sie einen Stift herstellen, wird der bearbeitete Stift mit der Stückliste benannt, wbei der Tintentank, der Knopf und andere Artikel, die in die Fertigung eingehen als Unterartikel verwaltet werden.
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ Sie sollten sich an eine spezielle Vorlage eines Tabellenblattes halten um den B
|
||||
|
||||
#### Schritt 2: Geben Sie Daten in die CSV-Datei ein.
|
||||
|
||||

|
||||
<img class="screenshot" alt="Bestandsabgleich" src="{{docs_base_url}}/assets/img/setup/stock-reco-data.png">
|
||||
|
||||
Das CSV-Format beachtet Groß- und Kleinschreibung. Verändern Sie nicht die Kopfbezeichnungen, die in der Vorlage vordefiniert wurden. In den Spalten "Artikelnummer" und "Lager" geben Sie bitte die richtige Artikelnummer und das Lager ein, so wie es in ERPNext bezeichnet wird. Für die Menge geben Sie den Lagerbestand, den Sie für diesen Artikel erfassen wollen, in einem bestimmten Lager ein. Wenn Sie die Menge oder den wertmäßigen Betrag eines Artikels nicht ändern wollen, dann lassen Sie den Eintrag leer.
|
||||
Anmerkung: Geben Sie keine "0" ein, wenn sie die Menge oder den wertmäßigen Betrag nicht ändern wollen. Sonst kalkuliert das System eine Menge von "0". Lassen Sie also das Feld leer!
|
||||
@@ -52,11 +52,12 @@ Notiz: Wenn Sie die bewerteten Beträge eines Artikels eingeben, können Sie zum
|
||||
|
||||
#### Schritt 4: Überprüfen Sie die Daten zum Bestandsabgleich
|
||||
|
||||

|
||||
<img class="screenshot" alt="Bestandsabgleich Überprüfung" src="{{docs_base_url}}/assets/img/setup/stock-reco-upload.gif">
|
||||
|
||||
### Bericht zum Lagerbuch
|
||||
|
||||

|
||||
<img class="screenshot" alt="Bestandsabgleich" src="{{docs_base_url}}/assets/img/setup/stock-reco-ledger.png">
|
||||
|
||||
|
||||
##### So arbeitet der Bestandsabgleich
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Unten Sehen Sie ein Beispiel eines Worklflows.
|
||||
|
||||
Wenn ein Benutzer einen Urlaub beantragt, dann wird seine Anfrage an die Personalabteilung weiter geleitet. Die Personalabteilung, repräsentiert durch einen Mitarbeiter der Personalabteilung, wird diese Anfrage dann entweder genehmigen oder ablehnen. Wenn dieser Prozess abgeschlossen ist, dann bekommt der Vorgesetzte des Benutzers (der Urlaubsgenehmiger) eine Mitteilung, dass die Personalabteilung den Antrag genehmigt oder abgelehnt hat. Der Vorgesetzte, der die genehmigende Instanz ist, wird dann den Antrag entweder genehmigen oder ablehnen. Dementsprechend bekommt der Benutzer dann eine Genehmigung oder eine Ablehnung.
|
||||
|
||||

|
||||
<img class="screenshot" alt="Workflow" src="{{docs_base_url}}/assets/img/setup/workflow-leave-fl.jpg">
|
||||
|
||||
Um einen Workflow und Übergangsregeln zu erstellen, gehen Sie zu:
|
||||
|
||||
@@ -37,14 +37,14 @@ Gehen Sie in das Modul Personalwesen und klicken Sie auf Urlaubsantrag. Beantrag
|
||||
|
||||
Wenn ein Urlaubsantrag übertragen wird, steht der Status in der Ecke der rechten Seite auf "Beantragt".
|
||||
|
||||

|
||||
<img class="screenshot" alt="Workflow" src="{{docs_base_url}}/assets/img/setup/workflow-3.png">
|
||||
|
||||
Wenn sich ein Mitarbeiter der Personalabteilung anmeldet, dann kann er den Antrag genehmigen oder ablehnen. Wenn eine Genehmigung erfolgt, wechselt der Status in der rechten Ecke zu "Genehmigt". Es wird jedoch ein blaues Informationssymbol angezeigt, welches aussagt, dass der Urlaubsantrag noch anhängig ist.
|
||||
|
||||

|
||||
<img class="screenshot" alt="Workflow" src="{{docs_base_url}}/assets/img/setup/workflow-4.png">
|
||||
|
||||
Wenn der Urlaubsgenehmiger die Seite mit den Urlaubsanträgen öffnet, kann er den Status von "Genehmigt" auf "Abgelehnt" ändern.
|
||||
|
||||

|
||||
<img class="screenshot" alt="Workflow" src="{{docs_base_url}}/assets/img/setup/workflow-5.png">
|
||||
|
||||
{next}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
ERPNext Web Portal gives your customers quick access to their Orders, Invoices and Shipments Customers can check the status of their orders, invoices, and shipping status by logging on to the web.
|
||||
|
||||
Once an order is raised, either using the Shopping Cart or from within
|
||||
ERPNext, your customer can view the order and keep an eye on the billing and
|
||||
shipment status. When the invoice and payment against these orders are
|
||||
submitted, the customer can see the updated status on the portal, at a glance.
|
||||
<img class="screenshot" alt="Customer Portal" src="{{docs_base_url}}/assets/img/website/portal-menu.png">
|
||||
|
||||
<img class="screenshot" alt="Customer Portal" src={{docs_base_url}}/assets/img/website/place-order.png)
|
||||
Once an order is raised, either using the Shopping Cart or from within ERPNext, your customer can view the order and keep an eye on the billing and shipment status. When the invoice and payment against these orders are submitted, the customer can see the updated status on the portal, at a glance.
|
||||
|
||||
<img class="screenshot" alt="Customer Portal" src="{{docs_base_url}}/assets/img/website/website-login.png">
|
||||
|
||||
#### Outstanding Sales Invoice
|
||||
|
||||
<img class="screenshot" alt="Customer Portal Order 1" src="{{docs_base_url}}/assets/img/website/invoice-unpaid.png">
|
||||
<img class="screenshot" alt="Customer Portal" src="{{docs_base_url}}/assets/img/website/invoice-unpaid.png">
|
||||
|
||||
#### Paid Sales Invoice
|
||||
|
||||
<img class="screenshot" alt="Customer Portal Order 1" src="{{docs_base_url}}/assets/img/website/invoice-paid.png">
|
||||
<img class="screenshot" alt="Customer Portal" src="{{docs_base_url}}/assets/img/website/invoice-paid.png">
|
||||
|
||||
{next}
|
||||
{next}
|
||||
@@ -12,4 +12,5 @@ human-resources
|
||||
customer-portal
|
||||
website
|
||||
using-erpnext
|
||||
regional
|
||||
customize-erpnext
|
||||
|
||||
@@ -6,7 +6,7 @@ functions. This diagram does not cover all the features of ERPNext.
|
||||

|
||||
|
||||
|
||||
[Full Resolution]({{docs_base_url}}/assets/old_images/erpnext/overview.png)
|
||||
<img class="screenshot" alt="Workflow" src="{{docs_base_url}}/assets/img/setup/overview.png">
|
||||
|
||||
_Note: Not all of the steps are mandatory. ERPNext allows you to freely skip
|
||||
steps if you want to simplify the process._
|
||||
|
||||
@@ -17,9 +17,15 @@ by:
|
||||
|
||||
* Select the Item to be produced.
|
||||
* The default BOM for that item will be fetched by the system. You can also change BOM.
|
||||
* Enter the Qty to manufacture.
|
||||
* If the selected BOM has operartion mentioned in it, the system shall fetch all operations from BOM.
|
||||
* Mention the Planned Start Date (an Estimated Date at which you want the Production to begin.)
|
||||
* Select Warehouses. Work-in-Progress Warehouse is where your Items will be transferred when you begin production and Target Warehouse is where you store finished Items before they are shipped.
|
||||
* Select Warehouses:
|
||||
* Source Warehouses: The warehouse where you store your raw materials. Each required item can have separate source warehouse. Group warehouse also can be selected as source warehouse. On submission of Production Order, the raw mateirals will be reserved in these warehouses for production usage.
|
||||
* Work-in-Progress Warehouse: The warehouse where your Items will be transferred when you begin production. Group Warehouse can also be selected as Work-in-Progress warehouse.
|
||||
* Target Warehouse: The warehouse where you store finished Items before they are shipped.
|
||||
* Scrap Warehouse: Scrap Items will be stored in this warehouse.
|
||||
* Required Items: All the required items (raw materials) will be fetched from BOM and populated in this table. Here you can also change the default source warehouse for any item. And during the production, you can track transferred raw materials from this table.
|
||||
|
||||
> Note : You can save a Production Order without selecting the warehouses, but warehouses are mandatory for submitting a Production Order
|
||||
|
||||
|
||||
0
erpnext/docs/user/manual/en/regional/__init__.py
Normal file
9
erpnext/docs/user/manual/en/regional/index.md
Normal file
@@ -0,0 +1,9 @@
|
||||
ERPNext aims to support local regulation for all the regions in the world. In most cases ERPNext is very flexible so you can easily add Custom Fields and make Custom Reports to support the regluation of your region.
|
||||
|
||||
As of mid-2017, ERPNext aims to pre-set additional fields and forms required for companies to easily submit reports to the relevant authorities.
|
||||
|
||||
This section is still under-development.
|
||||
|
||||
### Regions
|
||||
|
||||
{index}
|
||||
21
erpnext/docs/user/manual/en/regional/india/gst-remimders.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Sending GST Reminders
|
||||
|
||||
You can send email reminders to your Customers and Suppliers so that they can directly add or update their GSTIN numbers
|
||||
|
||||
To send GSTIN Reminders, you can either open the Customer / Supplier record or **GST Settings**
|
||||
|
||||
<img class="screenshot" alt="GST Settings" src="{{docs_base_url}}/assets/img/regional/india/gst-settings.png">
|
||||
|
||||
Here you can click on the "Send GSTIN Update Reminders" button to send email reminders to all your customers
|
||||
|
||||
### Updating GSTIN
|
||||
|
||||
Your customers will receive an email asking them to update their GSTIN and that email will link them to a portal page:
|
||||
|
||||
<img class="screenshot" alt="GST Portal Page" src="{{docs_base_url}}/assets/img/regional/india/gstin-portal-update.png">
|
||||
|
||||
Here they can update their GSTIN and it will automatically be added to your customer GSTIN record.
|
||||
|
||||
#### Sample GSTIN Reminder Email
|
||||
|
||||
<img class="screenshot" alt="GST Reminder Email" src="{{docs_base_url}}/assets/img/regional/india/gstin-reminder-email.png">
|
||||
80
erpnext/docs/user/manual/en/regional/india/gst-setup.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# GST Features in ERPNext
|
||||
|
||||
### 1. Setting up GSTIN
|
||||
|
||||
GST Law requires that you maintain the GSTIN number for all your suppliers and vendors. In ERPNext, GSTIN is linked to the **Address**
|
||||
|
||||
<img class="screenshot" alt="GST in Customer" src="{{docs_base_url}}/assets/img/regional/india/gstin-customer.gif">
|
||||
|
||||
**GST for your Company Address**
|
||||
|
||||
You also need to set the Address for your own Company and your Company's GST Number
|
||||
|
||||
Go to the Company master and add the GSTIN to your default address.
|
||||
|
||||
<img class="screenshot" alt="GST in Company" src="{{docs_base_url}}/assets/img/regional/india/gstin-company.gif">
|
||||
|
||||
**Include GSTIN number in the Address Template**
|
||||
|
||||
Open Address Template record for India, add GSTIN number and State Code there if not exists.
|
||||
|
||||
<img class="screenshot" alt="GST in Company" src="{{docs_base_url}}/assets/img/regional/india/address-template-gstin.png">
|
||||
|
||||
|
||||
### 2. Setting up HSN Codes
|
||||
|
||||
According to the GST Law, your itemised invoices must contain the HSN Code related to that Item. ERPNext comes pre-installed with all 12,000+ HSN Codes so that you can easily select the relevant HSN Code in your Item
|
||||
|
||||
<img class="screenshot" alt="HSN in Item" src="{{docs_base_url}}/assets/img/regional/india/hsn-item.gif">
|
||||
|
||||
### 3. Making Tax Masters
|
||||
|
||||
To setup Billing in GST, you need to create 3 Tax Accounts for the various GST reporting heads CGST - Central GST, SGST - State GST, IGST - Inter-state GST
|
||||
|
||||
Go to your **Chart of Accounts**, under the Duties and Taxes head of your account, create 3 Accounts
|
||||
|
||||
**Note:** Usually the rate in CGST and SGST is half of IGST. For example if most of your items are billed at 18%, then create IGST at 18%, CGST and SGST at 9% each.
|
||||
|
||||
<img class="screenshot" alt="GST in Customer" src="{{docs_base_url}}/assets/img/regional/india/gst-in-coa.png">
|
||||
|
||||
### 4. Make Tax Templates
|
||||
|
||||
You will have have to make two tax templates for both your sales and purchase, one for in state sales and other for out of state sales.
|
||||
|
||||
In your **In State GST** template, select 2 accounts, SGST and CGST
|
||||
|
||||
<img class="screenshot" alt="GST in Customer" src="{{docs_base_url}}/assets/img/regional/india/gst-template-in-state.png">
|
||||
|
||||
In your **Out of State GST** template, select IGST
|
||||
|
||||
### 5. Making GST Ready Invoices
|
||||
|
||||
If you have setup the GSTIN of your Customers and Suppliers, and your tax template, you are ready to go for making GST Ready Invoices!
|
||||
|
||||
For **Sales Invoice**,
|
||||
|
||||
1. Select the correct Customer and Item and the address where the transaction will happen.
|
||||
2. Check if the GSTIN of your Company and Supplier have been correctly set.
|
||||
3. Check if the HSN Number has been set in the Item
|
||||
4. Select the the **In State GST** or **Out of State GST** template that you have created based on the type of transaction
|
||||
5. Save and Submit the Invoice
|
||||
|
||||
<img class="screenshot" alt="GST Invoice" src="{{docs_base_url}}/assets/img/regional/india/gst-invoice.gif">
|
||||
|
||||
### 6. Print GST Tax Invoice
|
||||
|
||||
To print Tax Invoice as per GSTN guidelines, please select **GST Tax Invoice** print format. This print format includes company address, GSTIN numbers, HSN/SAC Code and item-wise tax breakup. And while printing select correct value of Invoice Copy field, to mention whether it is for the Customer, Supplier or Transporter.
|
||||
|
||||
<img class="screenshot" alt="Sample GST Tax Invoice" src="{{docs_base_url}}/assets/img/regional/india/sample-gst-tax-invoice.png">
|
||||
|
||||
### Reports
|
||||
|
||||
ERPNext comes with most of your reports you need to prepare your GST Returns. Go to Accounts > GST India head for the list.
|
||||
|
||||
<img class="screenshot" alt="GST Menus" src="{{docs_base_url}}/assets/img/regional/india/gst-menu.png">
|
||||
|
||||
You can check the impact of your invoice in the **GST Sales Register** and **GST Itemised Sales Register**
|
||||
|
||||
<img class="screenshot" alt="GST Itemised Sales Register" src="{{docs_base_url}}/assets/img/regional/india/gst-itemised.png">
|
||||
|
||||
|
||||
5
erpnext/docs/user/manual/en/regional/india/index.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Statutory Requirements for India
|
||||
|
||||
As of 2017, India will fall under the new GST (Goods and Services Tax) regime and ERPNext makes it easy for users to track the details of its Supplier and Customers across Invoices and make the required reports.
|
||||
|
||||
{index}
|
||||
2
erpnext/docs/user/manual/en/regional/india/index.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
gst-setup
|
||||
gst-reminders
|
||||
@@ -91,5 +91,5 @@ To setup Email Digests, go to:
|
||||
|
||||
#### Figure 4: Set up Email Digest
|
||||
|
||||

|
||||
<img class="screenshot" alt="Email Digest" src="{{docs_base_url}}/assets/img/setup/email/email-digest.png">
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ A predefined template of an spreadsheet file should be followed for importing it
|
||||
|
||||
#### Step 2: Enter Data in csv file.
|
||||
|
||||

|
||||
<img class="screenshot" alt="Stock Reconciliation" src="{{docs_base_url}}/assets/img/setup/stock-reco-data.png">
|
||||
|
||||
The csv format is case-sensitive. Do not edit the headers which are preset in the template. In the Item Code and Warehouse column, enter exact Item Code and Warehouse as created in your ERPNext account. For quatity, enter stock level you wish to set for that item, in a specific warehouse.
|
||||
|
||||
@@ -59,11 +59,12 @@ report. The report will show you all types of rates.
|
||||
|
||||
#### Step 4: Review the reconciliation data
|
||||
|
||||

|
||||
<img class="screenshot" alt="Stock Reconciliation" src="{{docs_base_url}}/assets/img/setup/stock-reco-upload.gif">
|
||||
|
||||
### Stock Ledger Report
|
||||
|
||||

|
||||
<img class="screenshot" alt="Stock Reconciliation" src="{{docs_base_url}}/assets/img/setup//stock-reco-ledger.png">
|
||||
|
||||
|
||||
**How Stock Reconciliation Works**
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ will get an indication that the HR department has Accepted or Rejected. The
|
||||
Manager, who is the approving authority, will either Approve or Reject this
|
||||
request. Accordingly,the user will get his Approved or Rejected status.
|
||||
|
||||

|
||||
<img class="screenshot" alt="Workflow" src="{{docs_base_url}}/assets/img/setup/workflow-leave-fl.jpg">
|
||||
|
||||
To make this Workflow and transition rules go to :
|
||||
|
||||
@@ -46,15 +46,15 @@ workflow transition step that says from submitted you can cancel.
|
||||
|
||||
When a Leave Application is saved by Employee, the status of the document changes to "Applied"
|
||||
|
||||

|
||||
<img class="screenshot" alt="Workflow" src="{{docs_base_url}}/assets/img/setup/workflow-3.png">
|
||||
|
||||
When the HR User logs in, he can either Approve or Reject. If approved the
|
||||
status of the document changes to "Approved by HR". However, it is yet to be approved by Leave Approver.
|
||||
|
||||

|
||||
<img class="screenshot" alt="Workflow" src="{{docs_base_url}}/assets/img/setup/workflow-4.png">
|
||||
|
||||
When the Leave Approver opens the Leave Application page, he can finally "Approve" or "Reject" the Leave Application.
|
||||
|
||||

|
||||
<img class="screenshot" alt="Workflow" src="{{docs_base_url}}/assets/img/setup/workflow-5.png">
|
||||
|
||||
{next}
|
||||
|
||||
@@ -28,15 +28,18 @@ In ERPNext, you can select different type of Warehouses to stock your different
|
||||
|
||||
These numbers help to track individual units or batches of Items which you sell. It also tracks warranty and returns. In case any individual Item is recalled by the supplier the number system helps to track individual Item. The numbering system also manages expiry dates. Please note that if you sell your items in thousands, and if the items are very small like pens or erasers, you need not serialize them. In ERPNext, you will have to mention the serial number in some accounting entries. To create serial numbers you will have to manually create all the numbers in your entries. If your product is not a big consumer durable Item, if it has no warranty and has no chances of being recalled, avoid giving serial numbers.
|
||||
|
||||
> Tip: While entering an item code in an items table, if the table requires inventory details, then depending on whether the entered item is batched or serialized, you can enter serial or batch numbers right away in a pop-up dialog.
|
||||
<img alt="Serial No modal" class="screenshot" src="{{docs_base_url}}/assets/img/stock/serial_no_modal.gif"><img alt="Batch No modal" class="screenshot" src="{{docs_base_url}}/assets/img/stock/batch_no_modal.png">
|
||||
|
||||
> Important: Once you mark an item as serialized or batched or neither, you cannot change it after you have made any stock entry.
|
||||
|
||||
* [Discussion on Serialized Inventory]({{docs_base_url}}/user/manual/en/setting-up/stock-reconciliation-for-non-serialized-item.html)
|
||||
|
||||
### Re Ordering
|
||||
|
||||
* **Re-order level** suggests the amount of stock balance in the Warehouse.
|
||||
* **Re-order Qty** suggests the amount of stock to be ordered to maintain minimum stock levels.
|
||||
* **Minimum Order Qty** is the minimum quantity for which a Material Request / Purchase Order must be made.
|
||||
***Re-order level** suggests the amount of stock balance in the Warehouse.
|
||||
***Re-order Qty** suggests the amount of stock to be ordered to maintain minimum stock levels.
|
||||
***Minimum Order Qty** is the minimum quantity for which a Material Request / Purchase Order must be made.
|
||||
|
||||
### Item Tax
|
||||
|
||||
@@ -52,6 +55,8 @@ Inspection Criteria: If a Quality Inspection is prepared for this Item, then thi
|
||||
|
||||
### Purchase Details
|
||||
|
||||
<img alt="Item Purchase Details" class="screenshot" src="{{docs_base_url}}/assets/img/stock/item-purchase.png">
|
||||
|
||||
<img class="screenshot" alt="Purchase details" src="{{docs_base_url}}/assets/img/stock/item-purchase.png">
|
||||
|
||||
* **Lead time days:** Lead time days are the number of days required for the Item to reach the warehouse.
|
||||
@@ -76,9 +81,13 @@ Inspection Criteria: If a Quality Inspection is prepared for this Item, then thi
|
||||
|
||||
* **Default Income Account:** Income account selected here will be fetched automatically in sales invoice for this item.
|
||||
|
||||
* **Cost Centre:** Cost center selected here will be fetched automatically in sales invoice for this item.
|
||||
<img class="screenshot" alt="Sales details" src="{{docs_base_url}}/assets/img/stock/item-sales.png)">
|
||||
|
||||
* **Customer Codes:** Track Item Code assigned by the Customers for this Item. This will help you in searching item while creating Sales Order based on the Item Code in the Customer's Purchase Order.
|
||||
<img class="screenshot" alt="Sales details" src="{{docs_base_url}}/assets/img/stock/item-sales.png">
|
||||
|
||||
***Cost Centre:** Cost center selected here will be fetched automatically in sales invoice for this item.
|
||||
|
||||
***Customer Codes:** Track Item Code assigned by the Customers for this Item. This will help you in searching item while creating Sales Order based on the Item Code in the Customer's Purchase Order.
|
||||
|
||||
<img class="screenshot" alt="Sales details" src="{{docs_base_url}}/assets/img/stock/item-sales.png)">
|
||||
|
||||
|
||||
8
erpnext/docs/user/manual/en/support/support_reports.md
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
### Support Hours
|
||||
This report provide the information about the time slot along with the count of issues has been reported during the slot daywise.
|
||||
|
||||
> Support > Reports > Support Hours
|
||||
|
||||
<img class="screenshot" alt="Maintenance Visit" src="{{docs_base_url}}/assets/img/support/support_hours.png">
|
||||
@@ -0,0 +1,16 @@
|
||||
Global-search.md
|
||||
|
||||
|
||||
Global search is a word-processing operation in which a complete computer file or set of files is searched for every occurrence of a particular word or other sequence of characters.
|
||||
|
||||
We have made the Awesome Bar of ERPNext lot more powerful by adding Global Search feature.
|
||||
Global Search helps users find information quickly. It’s located in the upper right-hand corner in ERPNext. Simply entering a few characters in the Search will show results from several different record types (Contact, Customer, Issues etc.) related to that keyword. You can also customise the fields based on which search will be shown.
|
||||
|
||||
### Using Awesome bar for Global Search.
|
||||
|
||||
<img alt="Global Search" class="screenshot" src="{{docs_base_url}}/assets/img/articles/Global Search .gif">
|
||||
|
||||
### Enable Global Search for fields in a Doctype.
|
||||
|
||||
<img alt="Global Search" class="screenshot" src="{{docs_base_url}}/assets/img/articles/Enable Global Search .gif">
|
||||
|
||||
@@ -160,6 +160,9 @@ doc_events = {
|
||||
},
|
||||
"Payment Entry": {
|
||||
"on_submit": "erpnext.accounts.doctype.payment_request.payment_request.make_status_as_paid"
|
||||
},
|
||||
'Address': {
|
||||
'validate': 'erpnext.regional.india.utils.validate_gstin_for_india'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,23 @@ frappe.provide("erpnext.bom");
|
||||
frappe.ui.form.on("BOM", {
|
||||
setup: function(frm) {
|
||||
frm.add_fetch('buying_price_list', 'currency', 'currency')
|
||||
frm.fields_dict["items"].grid.get_field("bom_no").get_query = function(doc, cdt, cdn){
|
||||
|
||||
frm.set_query("bom_no", "items", function() {
|
||||
return {
|
||||
filters: {'currency': frm.doc.currency}
|
||||
filters: {
|
||||
'currency': frm.doc.currency,
|
||||
'company': frm.doc.company
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
frm.set_query("source_warehouse", "items", function() {
|
||||
return {
|
||||
filters: {
|
||||
'company': frm.doc.company,
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onload_post_render: function(frm) {
|
||||
@@ -74,6 +86,15 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({
|
||||
|
||||
get_bom_material_detail(doc, cdt, cdn, scrap_items);
|
||||
},
|
||||
conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) {
|
||||
if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) {
|
||||
var item = frappe.get_doc(cdt, cdn);
|
||||
frappe.model.round_floats_in(item, ["qty", "conversion_factor"]);
|
||||
item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item));
|
||||
refresh_field("stock_qty", item.name, item.parentfield);
|
||||
this.toggle_conversion_factor(item);
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
$.extend(cur_frm.cscript, new erpnext.bom.BomController({frm: cur_frm}));
|
||||
@@ -300,6 +321,13 @@ frappe.ui.form.on("BOM Operation", "workstation", function(frm, cdt, cdn) {
|
||||
})
|
||||
});
|
||||
|
||||
frappe.ui.form.on("BOM Item", "qty", function(frm, cdt, cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
d.stock_qty = d.qty * d.conversion_factor;
|
||||
refresh_field("items");
|
||||
});
|
||||
|
||||
|
||||
frappe.ui.form.on("BOM Operation", "operations_remove", function(frm) {
|
||||
erpnext.bom.calculate_op_cost(frm.doc);
|
||||
erpnext.bom.calculate_total(frm.doc);
|
||||
|
||||
@@ -7,6 +7,7 @@ from frappe.utils import cint, cstr, flt
|
||||
from frappe import _
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
from frappe.website.website_generator import WebsiteGenerator
|
||||
from erpnext.stock.get_item_details import get_conversion_factor
|
||||
|
||||
from operator import itemgetter
|
||||
|
||||
@@ -48,7 +49,7 @@ class BOM(WebsiteGenerator):
|
||||
self.set_conversion_rate()
|
||||
|
||||
from erpnext.utilities.transaction_base import validate_uom_is_integer
|
||||
validate_uom_is_integer(self, "stock_uom", "qty", "BOM Item")
|
||||
validate_uom_is_integer(self, "stock_uom", "stock_qty", "BOM Item")
|
||||
|
||||
self.validate_materials()
|
||||
self.set_bom_material_details()
|
||||
@@ -60,6 +61,7 @@ class BOM(WebsiteGenerator):
|
||||
|
||||
def on_update(self):
|
||||
self.check_recursion()
|
||||
self.update_stock_qty()
|
||||
self.update_exploded_items()
|
||||
|
||||
def on_submit(self):
|
||||
@@ -94,7 +96,7 @@ class BOM(WebsiteGenerator):
|
||||
def set_bom_material_details(self):
|
||||
for item in self.get("items"):
|
||||
ret = self.get_bom_material_detail({"item_code": item.item_code, "item_name": item.item_name, "bom_no": item.bom_no,
|
||||
"qty": item.qty})
|
||||
"stock_qty": item.stock_qty})
|
||||
for r in ret:
|
||||
if not item.get(r):
|
||||
item.set(r, ret[r])
|
||||
@@ -122,8 +124,11 @@ class BOM(WebsiteGenerator):
|
||||
'description' : item and args['description'] or '',
|
||||
'image' : item and args['image'] or '',
|
||||
'stock_uom' : item and args['stock_uom'] or '',
|
||||
'uom' : item and args['stock_uom'] or '',
|
||||
'conversion_factor' : 1,
|
||||
'bom_no' : args['bom_no'],
|
||||
'rate' : rate,
|
||||
'stock_qty' : args.get("qty") or args.get("stock_qty") or 1,
|
||||
'base_rate' : rate if self.company_currency() == self.currency else rate * self.conversion_rate
|
||||
}
|
||||
return ret_item
|
||||
@@ -160,7 +165,7 @@ class BOM(WebsiteGenerator):
|
||||
|
||||
for d in self.get("items"):
|
||||
rate = self.get_bom_material_detail({'item_code': d.item_code, 'bom_no': d.bom_no,
|
||||
'qty': d.qty})["rate"]
|
||||
'stock_qty': d.stock_qty})["rate"]
|
||||
if rate:
|
||||
d.rate = rate
|
||||
|
||||
@@ -240,6 +245,19 @@ class BOM(WebsiteGenerator):
|
||||
frappe.db.get_value('Price List', self.buying_price_list, 'currency') != self.currency:
|
||||
frappe.throw(_("Currency of the price list {0} is not similar with the selected currency {1}").format(self.buying_price_list, self.currency))
|
||||
|
||||
|
||||
def update_stock_qty(self):
|
||||
for m in self.get('items'):
|
||||
|
||||
if not m.conversion_factor:
|
||||
m.conversion_factor = flt(get_conversion_factor(m.item_code, m.uom)['conversion_factor'])
|
||||
if m.uom and m.qty:
|
||||
m.stock_qty = flt(m.conversion_factor)*flt(m.qty)
|
||||
if not m.uom and m.stock_uom:
|
||||
m.uom = m.stock_uom
|
||||
m.qty = m.stock_qty
|
||||
|
||||
|
||||
def set_conversion_rate(self):
|
||||
self.conversion_rate = get_exchange_rate(self.currency, self.company_currency())
|
||||
|
||||
@@ -259,7 +277,7 @@ class BOM(WebsiteGenerator):
|
||||
for m in self.get('items'):
|
||||
if m.bom_no:
|
||||
validate_bom_no(m.item_code, m.bom_no)
|
||||
if flt(m.qty) <= 0:
|
||||
if flt(m.stock_qty) <= 0:
|
||||
frappe.throw(_("Quantity required for Item {0} in row {1}").format(m.item_code, m.idx))
|
||||
check_list.append(m)
|
||||
|
||||
@@ -351,9 +369,9 @@ class BOM(WebsiteGenerator):
|
||||
d.rate = self.get_bom_unitcost(d.bom_no)
|
||||
|
||||
d.base_rate = flt(d.rate) * flt(self.conversion_rate)
|
||||
d.amount = flt(d.rate, self.precision("rate", d)) * flt(d.qty, self.precision("qty", d))
|
||||
d.amount = flt(d.rate, self.precision("rate", d)) * flt(d.stock_qty, self.precision("stock_qty", d))
|
||||
d.base_amount = d.amount * flt(self.conversion_rate)
|
||||
d.qty_consumed_per_unit = flt(d.qty, self.precision("qty", d)) / flt(self.quantity, self.precision("quantity"))
|
||||
d.qty_consumed_per_unit = flt(d.stock_qty, self.precision("stock_qty", d)) / flt(self.quantity, self.precision("quantity"))
|
||||
total_rm_cost += d.amount
|
||||
base_total_rm_cost += d.base_amount
|
||||
|
||||
@@ -367,7 +385,7 @@ class BOM(WebsiteGenerator):
|
||||
|
||||
for d in self.get('scrap_items'):
|
||||
d.base_rate = d.rate * self.conversion_rate
|
||||
d.amount = flt(d.rate, self.precision("rate", d)) * flt(d.qty, self.precision("qty", d))
|
||||
d.amount = flt(d.rate, self.precision("rate", d)) * flt(d.stock_qty, self.precision("stock_qty", d))
|
||||
d.base_amount = d.amount * self.conversion_rate
|
||||
total_sm_cost += d.amount
|
||||
base_total_sm_cost += d.base_amount
|
||||
@@ -385,15 +403,16 @@ class BOM(WebsiteGenerator):
|
||||
self.cur_exploded_items = {}
|
||||
for d in self.get('items'):
|
||||
if d.bom_no:
|
||||
self.get_child_exploded_items(d.bom_no, d.qty)
|
||||
self.get_child_exploded_items(d.bom_no, d.stock_qty)
|
||||
else:
|
||||
self.add_to_cur_exploded_items(frappe._dict({
|
||||
'item_code' : d.item_code,
|
||||
'item_name' : d.item_name,
|
||||
'source_warehouse': d.source_warehouse,
|
||||
'description' : d.description,
|
||||
'image' : d.image,
|
||||
'stock_uom' : d.stock_uom,
|
||||
'qty' : flt(d.qty),
|
||||
'stock_qty' : flt(d.stock_qty),
|
||||
'rate' : d.base_rate,
|
||||
}))
|
||||
|
||||
@@ -402,16 +421,17 @@ class BOM(WebsiteGenerator):
|
||||
|
||||
def add_to_cur_exploded_items(self, args):
|
||||
if self.cur_exploded_items.get(args.item_code):
|
||||
self.cur_exploded_items[args.item_code]["qty"] += args.qty
|
||||
self.cur_exploded_items[args.item_code]["stock_qty"] += args.stock_qty
|
||||
else:
|
||||
self.cur_exploded_items[args.item_code] = args
|
||||
|
||||
def get_child_exploded_items(self, bom_no, qty):
|
||||
def get_child_exploded_items(self, bom_no, stock_qty):
|
||||
""" Add all items from Flat BOM of child BOM"""
|
||||
# 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.stock_uom, bom_item.qty, bom_item.rate,
|
||||
bom_item.qty / ifnull(bom.quantity, 1) as qty_consumed_per_unit
|
||||
child_fb_items = frappe.db.sql("""select bom_item.item_code, bom_item.item_name,
|
||||
bom_item.description, bom_item.source_warehouse,
|
||||
bom_item.stock_uom, bom_item.stock_qty, bom_item.rate,
|
||||
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)
|
||||
|
||||
@@ -419,9 +439,10 @@ class BOM(WebsiteGenerator):
|
||||
self.add_to_cur_exploded_items(frappe._dict({
|
||||
'item_code' : d['item_code'],
|
||||
'item_name' : d['item_name'],
|
||||
'source_warehouse' : d['source_warehouse'],
|
||||
'description' : d['description'],
|
||||
'stock_uom' : d['stock_uom'],
|
||||
'qty' : d['qty_consumed_per_unit']*qty,
|
||||
'stock_qty' : d['qty_consumed_per_unit'] * stock_qty,
|
||||
'rate' : flt(d['rate']),
|
||||
}))
|
||||
|
||||
@@ -433,8 +454,8 @@ class BOM(WebsiteGenerator):
|
||||
ch = self.append('exploded_items', {})
|
||||
for i in self.cur_exploded_items[d].keys():
|
||||
ch.set(i, self.cur_exploded_items[d][i])
|
||||
ch.amount = flt(ch.qty) * flt(ch.rate)
|
||||
ch.qty_consumed_per_unit = flt(ch.qty) / flt(self.quantity)
|
||||
ch.amount = flt(ch.stock_qty) * flt(ch.rate)
|
||||
ch.qty_consumed_per_unit = flt(ch.stock_qty) / flt(self.quantity)
|
||||
ch.docstatus = self.docstatus
|
||||
ch.db_insert()
|
||||
|
||||
@@ -468,33 +489,36 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
|
||||
query = """select
|
||||
bom_item.item_code,
|
||||
item.item_name,
|
||||
sum(bom_item.qty/ifnull(bom.quantity, 1)) * %(qty)s as qty,
|
||||
sum(bom_item.stock_qty/ifnull(bom.quantity, 1)) * %(qty)s as qty,
|
||||
item.description,
|
||||
item.image,
|
||||
item.stock_uom,
|
||||
item.default_warehouse,
|
||||
item.expense_account as expense_account,
|
||||
item.buying_cost_center as cost_center
|
||||
{select_columns}
|
||||
from
|
||||
`tab{table}` bom_item, `tabBOM` bom, `tabItem` item
|
||||
where
|
||||
bom_item.parent = bom.name
|
||||
and bom_item.docstatus < 2
|
||||
and bom_item.parent = %(bom)s
|
||||
bom_item.docstatus < 2
|
||||
and bom.name = %(bom)s
|
||||
and bom_item.parent = bom.name
|
||||
and item.name = bom_item.item_code
|
||||
and is_stock_item = 1
|
||||
{conditions}
|
||||
{where_conditions}
|
||||
group by item_code, stock_uom"""
|
||||
|
||||
if fetch_exploded:
|
||||
query = query.format(table="BOM Explosion Item",
|
||||
conditions="""and item.is_sub_contracted_item = 0""")
|
||||
where_conditions="""and item.is_sub_contracted_item = 0""",
|
||||
select_columns = ", bom_item.source_warehouse")
|
||||
items = frappe.db.sql(query, { "qty": qty, "bom": bom }, as_dict=True)
|
||||
elif fetch_scrap_items:
|
||||
query = query.format(table="BOM Scrap Item", conditions="")
|
||||
query = query.format(table="BOM Scrap Item", where_conditions="", select_columns="")
|
||||
items = frappe.db.sql(query, { "qty": qty, "bom": bom }, as_dict=True)
|
||||
else:
|
||||
query = query.format(table="BOM Item", conditions="")
|
||||
query = query.format(table="BOM Item", where_conditions="",
|
||||
select_columns = ", bom_item.source_warehouse")
|
||||
items = frappe.db.sql(query, { "qty": qty, "bom": bom }, as_dict=True)
|
||||
|
||||
for item in items:
|
||||
@@ -536,7 +560,7 @@ def get_children():
|
||||
return frappe.db.sql("""select
|
||||
bom_item.item_code,
|
||||
bom_item.bom_no as value,
|
||||
bom_item.qty,
|
||||
bom_item.stock_qty,
|
||||
if(ifnull(bom_item.bom_no, "")!="", 1, 0) as expandable,
|
||||
item.image,
|
||||
item.description
|
||||
|
||||
@@ -6,18 +6,20 @@
|
||||
"doctype": "BOM Item",
|
||||
"item_code": "_Test Serialized Item With Series",
|
||||
"parentfield": "items",
|
||||
"qty": 1.0,
|
||||
"stock_qty": 1.0,
|
||||
"rate": 5000.0,
|
||||
"stock_uom": "_Test UOM"
|
||||
"stock_uom": "_Test UOM",
|
||||
"source_warehouse": "_Test Warehouse - _TC"
|
||||
},
|
||||
{
|
||||
"amount": 2000.0,
|
||||
"doctype": "BOM Item",
|
||||
"item_code": "_Test Item 2",
|
||||
"parentfield": "items",
|
||||
"qty": 2.0,
|
||||
"stock_qty": 2.0,
|
||||
"rate": 1000.0,
|
||||
"stock_uom": "_Test UOM"
|
||||
"stock_uom": "_Test UOM",
|
||||
"source_warehouse": "_Test Warehouse - _TC"
|
||||
}
|
||||
],
|
||||
"docstatus": 1,
|
||||
@@ -35,7 +37,7 @@
|
||||
"doctype": "BOM Item",
|
||||
"item_code": "_Test Item Home Desktop 100",
|
||||
"parentfield": "items",
|
||||
"qty": 1.0,
|
||||
"stock_qty": 1.0,
|
||||
"rate": 2000.0,
|
||||
"stock_uom": "_Test UOM"
|
||||
}
|
||||
@@ -46,18 +48,20 @@
|
||||
"doctype": "BOM Item",
|
||||
"item_code": "_Test Item",
|
||||
"parentfield": "items",
|
||||
"qty": 1.0,
|
||||
"stock_qty": 1.0,
|
||||
"rate": 5000.0,
|
||||
"stock_uom": "_Test UOM"
|
||||
"stock_uom": "_Test UOM",
|
||||
"source_warehouse": "_Test Warehouse - _TC"
|
||||
},
|
||||
{
|
||||
"amount": 2000.0,
|
||||
"doctype": "BOM Item",
|
||||
"item_code": "_Test Item Home Desktop 100",
|
||||
"parentfield": "items",
|
||||
"qty": 2.0,
|
||||
"stock_qty": 2.0,
|
||||
"rate": 1000.0,
|
||||
"stock_uom": "_Test UOM"
|
||||
"stock_uom": "_Test UOM",
|
||||
"source_warehouse": "_Test Warehouse - _TC"
|
||||
}
|
||||
],
|
||||
"docstatus": 1,
|
||||
@@ -84,9 +88,10 @@
|
||||
"doctype": "BOM Item",
|
||||
"item_code": "_Test Item",
|
||||
"parentfield": "items",
|
||||
"qty": 1.0,
|
||||
"stock_qty": 1.0,
|
||||
"rate": 5000.0,
|
||||
"stock_uom": "_Test UOM"
|
||||
"stock_uom": "_Test UOM",
|
||||
"source_warehouse": "_Test Warehouse - _TC"
|
||||
},
|
||||
{
|
||||
"amount": 2000.0,
|
||||
@@ -94,9 +99,10 @@
|
||||
"doctype": "BOM Item",
|
||||
"item_code": "_Test Item Home Desktop Manufactured",
|
||||
"parentfield": "items",
|
||||
"qty": 3.0,
|
||||
"stock_qty": 3.0,
|
||||
"rate": 1000.0,
|
||||
"stock_uom": "_Test UOM"
|
||||
"stock_uom": "_Test UOM",
|
||||
"source_warehouse": "_Test Warehouse - _TC"
|
||||
}
|
||||
],
|
||||
"docstatus": 1,
|
||||
@@ -124,9 +130,10 @@
|
||||
"doctype": "BOM Item",
|
||||
"item_code": "_Test Item",
|
||||
"parentfield": "items",
|
||||
"qty": 2.0,
|
||||
"stock_qty": 2.0,
|
||||
"rate": 3000.0,
|
||||
"stock_uom": "_Test UOM"
|
||||
"stock_uom": "_Test UOM",
|
||||
"source_warehouse": "_Test Warehouse - _TC"
|
||||
}
|
||||
],
|
||||
"docstatus": 1,
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "hash",
|
||||
"beta": 0,
|
||||
"creation": "2013-03-07 11:42:57",
|
||||
"custom": 0,
|
||||
"default_print_format": "Standard",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -44,6 +46,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -72,6 +75,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -101,6 +105,38 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "source_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": "Source Warehouse",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Warehouse",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -129,6 +165,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -161,6 +198,7 @@
|
||||
"width": "300px"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -189,6 +227,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -218,6 +257,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -248,6 +288,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -276,11 +317,12 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "qty",
|
||||
"fieldname": "stock_qty",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@@ -289,7 +331,7 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Qty",
|
||||
"label": "Stock Qty",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "qty",
|
||||
@@ -306,6 +348,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -337,6 +380,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -365,6 +409,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -393,6 +438,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -424,6 +470,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -455,17 +502,17 @@
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-02-17 17:27:43.757983",
|
||||
"modified": "2017-07-04 17:51:18.151002",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM Explosion Item",
|
||||
|
||||