Compare commits

...

141 Commits

Author SHA1 Message Date
mbauskar
a8406e1544 Merge branch 'develop' 2017-07-11 13:02:15 +05:30
mbauskar
b2aa867b70 bumped to version 8.3.6 2017-07-11 13:32:14 +06:00
Makarand Bauskar
30f2bcbccc [hotfix] allow only 5 character in company_abbr field in setup wizard (#9779) 2017-07-11 12:36:57 +05:30
Prateeksha Singh
7d885432eb [wiz] add user slide, enforce first entry, static labels (#9744) 2017-07-11 12:36:34 +05:30
Nabin Hait
3b5f774144 Merge pull request #9410 from manassolanki/patch-company
Patch for the PR 8754, fixes #9011
2017-07-11 11:35:29 +05:30
Nabin Hait
3a200bbc44 Update create_address_doc_from_address_field_in_company.py 2017-07-11 11:34:27 +05:30
Nabin Hait
2bedca04ae Merge pull request #9750 from rohitwaghchaure/taxable_amount_issue
[Fix] Taxable amount in tax breakup showing wrong value for duplicate items in the invoice
2017-07-11 11:16:56 +05:30
Manas Solanki
1a0536bff4 changes as per review 2017-07-11 11:16:16 +05:30
Manas Solanki
cdba021802 patch for removing the address field from company and creating address doc, fixes #9011 2017-07-11 11:16:16 +05:30
Shivam Mishra
3fe5ecc611 Added Lead fixture, Added test for Lead, Opportunity (#9607)
* Added fixtures for Address and Contact

* Fixed formatting errors

* Added Test for terms, Added Tax and Terms fixture

* Add tests for tax

* Added Test for Print Format
2017-07-11 10:33:07 +05:30
Rohit Waghchaure
296fbfeaac [Fix] Taxable amount in tax breakup showing wrong value for duplicate items in the invoice 2017-07-10 23:06:49 +05:30
Rushabh Mehta
4a7b4efbec [tests] update travis.yml to run ui-tests (#9748)
* [tests] update travis.yml to run ui-tests

* [tests] update travis.yml to run ui-tests

* [test] fix item_group.py

* [test] fix item_group.py

* [check] daily work summary fails?

* [check] daily work summary fails?

* [check] daily work summary fails?

* [check] daily work summary fails?

* [check] daily work summary fails?

* [check] daily work summary fails?

* [check] no scheduled jobs for tests

* [check] daily work summary fails?

* [check] daily work summary fails?
2017-07-10 23:00:01 +05:30
Nabin Hait
f7d2a59c18 Merge pull request #9538 from sagarvora/fix-bom-issue
[minor] fix bom price resetting on validation
2017-07-10 20:41:16 +05:30
Nabin Hait
0ac8542eaa Merge pull request #9772 from tundebabzy/issue-9595
Wrong Exchange Rate when making payment against Journal Entry (#9595)
2017-07-10 19:12:48 +05:30
Nabin Hait
c8b6d3badb Merge pull request #9713 from manassolanki/stud-dashb
changes in the student dashboard
2017-07-10 19:10:57 +05:30
Nabin Hait
cdf4320b3b Merge pull request #9673 from rohitwaghchaure/serial_no_sales_return_issue
Remove sales invoice from serial number while making sales return entery using delivery note
2017-07-10 19:04:30 +05:30
Nabin Hait
5b2d3222f3 Merge pull request #9710 from rohitwaghchaure/sales_invoice_dn_link_revert
Revert sales invoice dn link issue
2017-07-10 18:47:38 +05:30
Manas Solanki
5d202ca31a change in the student type 2017-07-10 18:40:53 +05:30
Nabin Hait
7088db9e1f Merge pull request #9773 from mbauskar/docs
[docs] corrected the filename from sms_setting2 -> sms_settings2
2017-07-10 18:17:28 +05:30
Nabin Hait
ec344ffa96 Merge pull request #9761 from mbauskar/depends-on
[minor] fixed depends on for stock entry expense_account and purchase receipt item's cost_center field
2017-07-10 18:16:15 +05:30
mbauskar
a52e726b6b [docs] corrected the filename from sms_setting2 -> sms_settings2 2017-07-10 18:11:32 +05:30
Nabin Hait
cf82c3828e Merge pull request #9769 from manassolanki/payment-entry
frappe call if posting date is defined
2017-07-10 17:57:26 +05:30
Nabin Hait
6fcfbaa1f9 Merge pull request #9766 from nabinhait/migration_fixes
minor fixes while upgrading an old instance
2017-07-10 17:45:25 +05:30
Nabin Hait
a6c733d06c Merge pull request #9753 from rohitwaghchaure/budget_cost_center_issue
[Fix] Budget against the field in the validation message
2017-07-10 17:45:02 +05:30
tunde
46ef26df71 treat use case where Journal entry is contained in outstanding_invoices in get_outstanding_reference_documents function 2017-07-10 13:05:29 +01:00
Manas Solanki
99d571a786 frappe call if posting date is defined 2017-07-10 17:33:27 +05:30
Rohit Waghchaure
e3ae600277 [Fix] Budget against the field in the validation message 2017-07-10 17:06:54 +05:30
Nabin Hait
dd7a723214 minor fixes while upgrading an old instance 2017-07-10 16:41:59 +05:30
mbauskar
ccaf36a00f [minor] fixed depends on for stock entry expense_account and purchase receipt item's cost_center field 2017-07-10 16:24:24 +05:30
Manas Solanki
101a021f7b added student type in program enrollment and changes in the student dashboard 2017-07-10 16:21:44 +05:30
Nabin Hait
68ed0488a3 Merge pull request #9757 from mbauskar/breadcrumb-fixes
[hotfix] check if the item group exist or not
2017-07-10 15:32:52 +05:30
mbauskar
75a233b472 [hotfix] check if the item group exist or not 2017-07-10 14:36:42 +05:30
Nabin Hait
5a174d61bc Merge pull request #9754 from creamdory/develop
Added Quotation as Standard Sidebar Menu
2017-07-10 14:13:35 +05:30
creamdory
7a2815299e Added Quotation as Standard Sidebar Menu 2017-07-10 16:09:03 +08:00
Manas Solanki
195d2b577f add few details in the student log (#9670) 2017-07-10 12:29:46 +05:30
Britlog
8c85562ceb Breadcrumbs management (#9544) 2017-07-10 12:14:05 +05:30
Manas Solanki
77aa4762b8 don't request another ajax call if one is pending (#9620) 2017-07-10 12:12:16 +05:30
Nabin Hait
35ecab6a52 Merge branch 'develop' 2017-07-08 13:55:41 +05:30
Nabin Hait
097da8cc89 bumped to version 8.3.5 2017-07-08 14:25:41 +06:00
Nabin Hait
1d52a4df22 Merge pull request #9732 from adityaduggal/gstin_NA
First 2 digit validation would disregard NA values
2017-07-08 13:52:35 +05:30
Nabin Hait
cd61a20fb4 Update utils.py 2017-07-08 13:52:13 +05:30
Nabin Hait
8f7eb358b8 Merge pull request #9730 from nabinhait/prod_fix
Finish prod order when skipped material transfer
2017-07-08 13:50:52 +05:30
Aditya Duggal
0e285265b1 First 2 digit validation would disregard NA values 2017-07-08 13:35:01 +05:30
Nabin Hait
b866fcf14f Finish prod order when skipped material transfer 2017-07-08 12:57:13 +05:30
Manas Solanki
3d190a15ab changes in the student dashboard 2017-07-07 17:29:29 +05:30
mbauskar
ab59e4769b Merge branch 'develop' 2017-07-07 17:12:37 +05:30
mbauskar
7b5ca3e494 bumped to version 8.3.4 2017-07-07 17:42:36 +06:00
Rohit Waghchaure
8f2c8f6e9d Revert sales invoice dn link issue 2017-07-07 17:07:59 +05:30
Nabin Hait
76c5924cbe Merge pull request #9699 from manassolanki/productn
frappe call only when there is item in production order
2017-07-07 15:48:37 +05:30
Nabin Hait
6eb55042d8 Merge pull request #9704 from rohitwaghchaure/serial_no_not_found_issue
Fixed the issue Serial No serial no not found
2017-07-07 15:47:25 +05:30
Rohit Waghchaure
9589527784 Fixed the issue Serial No serial no not found 2017-07-07 15:34:28 +05:30
Nabin Hait
dba3f0048b Merge pull request #9702 from mbauskar/installation-note
[hotfix] fixed the customer address set filter on Installation Note
2017-07-07 15:28:23 +05:30
mbauskar
3f6a5b2539 [hotfix] fixed the customer address set filter on Installation Note 2017-07-07 15:24:01 +05:30
Manas Solanki
cf7f72e586 frappe call only when there is item 2017-07-07 15:09:57 +05:30
mbauskar
74d07c695b Merge branch 'develop' 2017-07-07 14:06:45 +05:30
mbauskar
5cf3868d03 bumped to version 8.3.3 2017-07-07 14:36:45 +06:00
Nabin Hait
1b36336fc3 Added new option in invoice_copy, made state_number read_only (#9695)
* Added new option in invoice_copy, made state_number read_only

* removed the trailing whitespace
2017-07-07 14:05:33 +05:30
mbauskar
3f3ac5652f resolved merge conflicts 2017-07-07 13:42:54 +05:30
mbauskar
7b8d366e3a bumped to version 8.3.2 2017-07-07 14:11:45 +06:00
rohitwaghchaure
a29442d6bf [Fix] Setup wizard translation (#9693) 2017-07-07 13:40:13 +05:30
rohitwaghchaure
bbe16c80ff [Fix] Setup wizard translation issue (#9692) 2017-07-07 13:27:15 +05:30
Nabin Hait
4e21f11864 Merge pull request #9690 from rmehta/additional-discount-fix
[fix] reset flag after promise
2017-07-07 13:26:14 +05:30
Rushabh Mehta
19d52dc503 [fix] remove extra flag 2017-07-07 13:23:23 +05:30
Nabin Hait
701f7cccbf Merge pull request #9688 from nabinhait/pos_fixes
Fixed itemised tax calc to fix pos loading
2017-07-07 13:18:14 +05:30
Nabin Hait
8b486b0f28 Fixed state code of WB and allowed invoice_copy to edit after submit 2017-07-07 13:14:25 +05:30
Rushabh Mehta
6705ab3eaf [fix] discount setting multiple times 2017-07-07 13:14:08 +05:30
Rushabh Mehta
233a19a373 [fix] reset flag after promise 2017-07-07 13:06:29 +05:30
Nabin Hait
fbb5945ff3 Fixed itemised tax calc to fix pos loading 2017-07-07 12:58:22 +05:30
Nabin Hait
7773ee8960 Sales invoice dashboard, multiple letter head in report and email alert docs 2017-07-07 12:49:05 +05:30
Nabin Hait
8ad168ac67 Fixed merge conflict 2017-07-06 23:18:00 +05:30
Nabin Hait
3e9520b276 bumped to version 8.2.5 2017-07-06 23:46:40 +06:00
Nabin Hait
29b74b90c8 bumped to version 8.3.1 2017-07-06 23:45:28 +06:00
Nabin Hait
ad6cf4fa88 Merge branch 'develop' 2017-07-06 23:15:28 +05:30
Nabin Hait
e927b81292 Update production_order.js 2017-07-06 23:11:54 +05:30
Makarand Bauskar
88a37c17a6 [hotfix] commit after every 200 production orders (#9674) 2017-07-06 19:13:09 +05:30
Rohit Waghchaure
934e69fe0b Remove sales invoice from serial number while making sales return entery using delivery note 2017-07-06 18:19:18 +05:30
Nabin Hait
5fe7f6c3fd Invoice copy is in the center in gst print format 2017-07-06 17:51:05 +05:30
Nabin Hait
d59819d9bb Date format issue fixed in setup wizard 2017-07-06 16:12:59 +05:30
mbauskar
afdd7a626c Merge branch 'develop' 2017-07-06 16:12:02 +05:30
mbauskar
7699b5e92a bumped to version 8.3.0 2017-07-06 16:42:02 +06:00
Nabin Hait
b5d765d19a Merge pull request #9669 from rohitwaghchaure/sales_invoice_serial_no_revert
Revert sales invoice serial no issues
2017-07-06 16:07:46 +05:30
Rohit Waghchaure
6d03790b44 Revert sales invoice serial no issues 2017-07-06 15:58:50 +05:30
Nabin Hait
3638250f5d Merge pull request #9641 from manassolanki/att-report
show inactive students in the monthly attendance report
2017-07-06 15:53:16 +05:30
Nabin Hait
7bf192e46a Merge pull request #9526 from manassolanki/validate-criteria
validate the duplicate assessment criteria
2017-07-06 15:47:10 +05:30
Nabin Hait
fa351f4bdf Merge pull request #9433 from tundebabzy/issue-9424
Errors due to a single quote in Supplier's name #9424
2017-07-06 15:22:16 +05:30
rohitwaghchaure
bedb486c55 [Fix] Multi letter head showing in the print (#9644) 2017-07-06 14:50:38 +05:30
Nabin Hait
f3f0dfef2a Invoice copy, state code in gst print format, hsn code in other sales/purchase docs (#9658)
* Invoice copy, state code in gst print format, hsn code in other sales/purchase docs

* Formatted Net Amount in item-tax-breakup

* GST print format fixes

* removed trailing whitespace

* removed trailing whitespace
2017-07-06 14:49:34 +05:30
rohitwaghchaure
5d5a81f375 Fixed patch create_warehouse_nestedset (#9656) 2017-07-06 13:03:11 +05:30
Nabin Hait
02c8607d04 Production Order Documentation for Required Items table (#9647) 2017-07-06 11:28:07 +05:30
Makarand Bauskar
ddd4845420 [hotfix] setup wizard, complete_setup fixes (#9654)
* [hotfix] setup wizard, complete_setup fixes

* Update utils.py

* Update utils.py
2017-07-06 11:09:34 +05:30
Rushabh Mehta
63b06420e1 [fix] setup wizard dates (#9646) 2017-07-05 17:56:04 +05:30
Makarand Bauskar
bf66d7e041 [minor] setup wizard dates to yyyy-mm-dd format (#9642) 2017-07-05 17:26:34 +05:30
Manas Solanki
fc85768685 show inactive students in the monthly attendance reoprt 2017-07-05 16:35:47 +05:30
Nabin Hait
37c784eb3a Updated modified in bom doctypes 2017-07-05 16:09:23 +05:30
Makarand Bauskar
a54eb7d022 [minor] added change log for v8.3.0 (#9629) 2017-07-05 15:36:38 +05:30
Makarand Bauskar
48af4289d3 Revert "[enhance] Provision to set the currency in the pricing rule for the price" (#9636) 2017-07-05 15:17:02 +05:30
Rushabh Mehta
03c30a7f92 [minor] throw name error if group matches with name, for setup wizard to nicely catch the exception 2017-07-05 15:12:07 +05:30
Nabin Hait
949a920022 Production Order Enhancements (#9432)
* Production Order Enhancements
  - Show required items child table
  - Source warehouse for each raw materials, in Pro Order Item and BOM Item table
  - Group warehouse allowed for source and wip warehouse
  - Patch to populate required items, to fix status and reserved qty for stopped pro order
  - Cleaned up existing codes
  - Test cases

* Set available qty in source and wip warehouse

* minor fix in bom query naming

* Minor Fixes

* Reload BOM doctypes in patch
2017-07-05 13:55:41 +05:30
Nabin Hait
852cb64e4f GST tax invoice print format and more (#9616)
* GST Tax Invoice print format and more. Fixes #9545 #9566 #9608

* Reload gst print format only for Indian users

* Fixes as Codacy
2017-07-05 12:58:19 +05:30
mbauskar
b66fb9a4c0 Merge branch 'master' into develop 2017-07-05 11:25:15 +05:30
Doridel Cahanap
5ad4a6e161 Total Stock Summary (#9465)
* Total Stock Summary

* indentation fixes and removed the for loop

* minor fixes in total stock summery report
2017-07-05 10:49:09 +05:30
Frappe PR Bot
3ceab64bfa [translation] translation updates (#9611) 2017-07-04 16:08:14 +05:30
tundebabzy
6015f0f2ec Inspection required before delivery isn't working for item variants (#9362)
* makes `copy_attributes_to_variant` to not ignore "Table"

* fixes test cases - `test_auto_material_request` and `test_auto_material_request_for_variant`

* adds test case - tables in templates should be copied to variants

* [ci] use deprecated trusty build for now
2017-07-04 15:43:02 +05:30
Prateeksha Singh
5e4c8ecd62 Setup wizard refactor (#9441)
* cleaned up slides, yet to decide on master data

* remove item_count

* [fix] refresh slides on domain bug

* UI test

* set header image here in ERPNext

* [minor] lint checks

* [wiz] change autofilled fiscal year format

* [minor] fixed date format to dd-mm-yyyy if fiscal year is not available for country
2017-07-03 18:23:57 +05:30
Makarand Bauskar
11df066ed0 Merge pull request #9586 from Arundhatii/28June
Added help page for Global Search and updated old images #9241
2017-07-03 16:24:39 +05:30
Makarand Bauskar
87f2848aeb resolved conflicts 2017-07-03 16:24:01 +05:30
Makarand Bauskar
12450fa473 Merge pull request #9558 from mbauskar/domainify-school
[domainify] Domainification for the Education Domain
2017-07-03 16:17:21 +05:30
Frappe
67bbcf6932 Resolved conflicts in index.md 2017-07-03 16:00:07 +05:30
Frappe
79918c66c2 Deleted old Images and added Global Search Help page 2017-07-03 15:58:14 +05:30
Makarand Bauskar
960208bae0 Merge pull request #9426 from tundebabzy/issue-9096
Gross Profit Does not display Reversal of sales invoice #9096
2017-07-03 15:23:21 +05:30
Makarand Bauskar
1bad1fcc00 Merge pull request #9452 from faztp12/develop
Customer Query include Search fields
2017-07-03 15:20:23 +05:30
Rushabh Mehta
43ef4e9047 Added first UI tests (#9532)
* [wip]

* [tests] wip

* [ui-tests] first-cut

* [minor] remove old tests
2017-07-03 11:53:07 +05:30
Makarand Bauskar
65f6f2a05e Merge pull request #9568 from frappe/add-code-of-conduct-1
Create CODE_OF_CONDUCT.md
2017-07-03 10:48:16 +05:30
Rushabh Mehta
558fcc140f Create CODE_OF_CONDUCT.md 2017-07-01 21:35:10 +05:30
mbauskar
a06a600d0a Merge branch 'master' into develop 2017-07-01 14:16:46 +05:30
Makarand Bauskar
1589ada04d Merge pull request #9473 from rohitwaghchaure/pricing_rule_currency
[enhance] Provision to set the currency in the pricing rule for the price
2017-06-30 18:58:30 +05:30
mbauskar
ee368221cf [domainify] Domainification for schools doctype 2017-06-30 17:58:57 +05:30
Charles-Henri Decultot
53de2f1b57 Translation correction in P&L report (#9436) 2017-06-30 16:45:11 +05:30
Sagar Vora
8ebc0df65f [minor] fix autoname for GST HSN Code (#9536) 2017-06-30 16:42:26 +05:30
Makarand Bauskar
4095915bff Merge pull request #9489 from rohitwaghchaure/billed_amt_issue_in_pr
[fix] Billed Amt does not get updated in Purchase Receipt, if Invoice exists and item has been returned once before
2017-06-30 16:02:31 +05:30
Makarand Bauskar
abe8e2ecce Merge pull request #9533 from rohitwaghchaure/total_amount_issue
[minor] Total amount not showing value in bank reconcillation
2017-06-30 14:50:23 +05:30
Makarand Bauskar
b053674781 Merge pull request #9534 from manassolanki/report
Course wise assessment result tool
2017-06-30 14:47:46 +05:30
Manas Solanki
bb68f8ca01 cleanup for the assessment report and added chart 2017-06-30 12:22:51 +05:30
Manas Solanki
85480b3af7 Added report in config/schools.py 2017-06-30 11:36:57 +05:30
Manas Solanki
c4125b32ee Course wise assessment reports 2017-06-30 11:36:57 +05:30
Sagar Vora
7640858510 [minor] fix bom price resetting on validation 2017-06-29 22:20:07 +05:30
Rohit Waghchaure
b801357ecf [minor] Total amount not showing value in bank reconcillation 2017-06-29 18:40:01 +05:30
Manas Solanki
800c69eec3 validate the duplicate assessment criteria 2017-06-29 13:00:22 +05:30
Rohit Waghchaure
eb7eb43dfc [fix] Billed Amt does not get updated in Purchase Receipt, if Invoice exists and item has been returned once before 2017-06-27 17:34:44 +05:30
Rohit Waghchaure
0b078fb4cb [enhance] Provision to set the currency in the pricing rule for the price 2017-06-27 00:06:40 +05:30
tunde
6837e69187 Merge branch 'develop' into issue-9424 2017-06-26 13:05:27 +01:00
tunde
5824ae98d9 calls local to build frappe.form_dict in order to pass failing test 2017-06-26 12:42:11 +01:00
tunde
67cbeb1bb0 Merge branch 'develop' into issue-9096 2017-06-26 12:11:07 +01:00
tunde
8859eb23a3 set form_dict correctly 2017-06-26 12:01:41 +01:00
tunde
ea7768d3f3 fixes form_dict failure in travis test 2017-06-26 10:17:53 +01:00
tunde
c1d22adb9f Merge branch 'develop' into issue-9424 2017-06-26 08:52:55 +01:00
tunde
bce13cd0e3 fixes failing test 2017-06-26 08:43:25 +01:00
Console Admin
8623166dca Customer Query include Searchfields
frappe/erpnext#8821
2017-06-23 20:32:52 +03:00
tunde
94500fd17b adds test case 2017-06-22 17:34:54 +01:00
tunde
dcd54209fc parameterises sql string 2017-06-22 14:03:19 +01:00
tunde
96e629a635 adds credits notes to report since double entry for P or L affects sales 2017-06-22 10:04:33 +01:00
253 changed files with 45332 additions and 41518 deletions

View File

@@ -131,6 +131,7 @@
"getCookies": true,
"get_url_arg": true,
"get_server_fields": true,
"set_multiple": true
"set_multiple": true,
"QUnit": true
}
}

View File

@@ -1,6 +1,12 @@
language: python
dist: trusty
group: deprecated-2017Q2
addons:
apt:
sources:
- google-chrome
packages:
- google-chrome-stable
python:
- "2.7"
@@ -8,43 +14,43 @@ python:
services:
- mysql
before_install:
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
install:
- sudo rm /etc/apt/sources.list.d/docker.list
- sudo apt-get purge -y mysql-common mysql-server mysql-client
- nvm install v7.10.0
# - wget https://raw.githubusercontent.com/frappe/bench/master/install_scripts/setup_frappe.sh
# - sudo bash setup_frappe.sh --skip-setup-bench --mysql-root-password travis --bench-branch develop
- wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py
- sudo python install.py --develop --user travis --without-bench-setup
- sudo pip install -e ~/bench
# - sudo pip install --upgrade pip
- rm $TRAVIS_BUILD_DIR/.git/shallow
- bash $TRAVIS_BUILD_DIR/travis/bench_init.sh
- cp -r $TRAVIS_BUILD_DIR/test_sites/test_site ~/frappe-bench/sites/
script:
before_script:
- wget http://chromedriver.storage.googleapis.com/2.27/chromedriver_linux64.zip
- unzip chromedriver_linux64.zip
- sudo apt-get install libnss3
- sudo apt-get --only-upgrade install google-chrome-stable
- sudo cp chromedriver /usr/local/bin/.
- sudo chmod +x /usr/local/bin/chromedriver
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- sleep 3
- mysql -u root -ptravis -e 'create database test_frappe'
- echo "USE mysql;\nCREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe';\nFLUSH PRIVILEGES;\n" | mysql -u root -ptravis
- echo "USE mysql;\nGRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost';\n" | mysql -u root -ptravis
- cd ~/frappe-bench
- bench get-app erpnext $TRAVIS_BUILD_DIR
- bench use test_site
- bench reinstall --yes
- bench build
- bench scheduler disable
- bench start &
- sleep 10
- bench --verbose run-tests --driver Firefox
before_script:
- mysql -u root -ptravis -e 'create database test_frappe'
- echo "USE mysql;\nCREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe';\nFLUSH PRIVILEGES;\n" | mysql -u root -ptravis
- echo "USE mysql;\nGRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost';\n" | mysql -u root -ptravis
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/92b3bea86d8c5397beef
on_success: always
on_failure: always
on_start: never
script:
- set -e
- bench --verbose run-tests
- sleep 5
- bench --verbose run-tests --ui-tests

46
CODE_OF_CONDUCT.md Normal file
View 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/

View File

@@ -2,8 +2,7 @@
from __future__ import unicode_literals
import frappe
__version__ = '8.2.4'
__version__ = '8.3.6'
def get_default_company(user=None):
'''Get default company for user'''

View File

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

View File

@@ -83,7 +83,7 @@ def validate_expense_against_budget(args):
budget_records = frappe.db.sql("""
select
b.{budget_against_field}, ba.budget_amount, b.monthly_distribution,
b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution,
b.action_if_annual_budget_exceeded,
b.action_if_accumulated_monthly_budget_exceeded
from
@@ -111,15 +111,15 @@ def validate_budget_records(args, budget_records):
args["month_end_date"] = get_last_day(args.posting_date)
compare_expense_with_budget(args, budget_amount,
_("Accumulated Monthly"), monthly_action)
_("Accumulated Monthly"), monthly_action, budget.budget_against)
if yearly_action in ("Stop", "Warn") and monthly_action != "Stop" \
and yearly_action != monthly_action:
compare_expense_with_budget(args, flt(budget.budget_amount),
_("Annual"), yearly_action)
_("Annual"), yearly_action, budget.budget_against)
def compare_expense_with_budget(args, budget_amount, action_for, action):
def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against):
actual_expense = get_actual_expense(args)
if actual_expense > budget_amount:
diff = actual_expense - budget_amount
@@ -127,7 +127,7 @@ def compare_expense_with_budget(args, budget_amount, action_for, action):
msg = _("{0} Budget for Account {1} against {2} {3} is {4}. It will exceed by {5}").format(
_(action_for), frappe.bold(args.account), args.budget_against_field,
frappe.bold(args.budget_against),
frappe.bold(budget_against),
frappe.bold(fmt_money(budget_amount, currency=currency)),
frappe.bold(fmt_money(diff, currency=currency)))

View File

@@ -140,6 +140,33 @@ class TestBudget(unittest.TestCase):
budget.load_from_db()
budget.cancel()
def test_monthly_budget_against_parent_group_cost_center(self):
cost_center = "_Test Cost Center 3 - _TC"
if not frappe.db.exists("Cost Center", cost_center):
frappe.get_doc({
'doctype': 'Cost Center',
'cost_center_name': '_Test Cost Center 3',
'parent_cost_center': "_Test Company - _TC",
'company': '_Test Company',
'is_group': 0
}).insert(ignore_permissions=True)
budget = make_budget("Cost Center", cost_center)
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, cost_center)
self.assertRaises(BudgetError, jv.submit)
budget.load_from_db()
budget.cancel()
jv.cancel()
frappe.delete_doc('Journal Entry', jv.name)
frappe.delete_doc('Cost Center', cost_center)
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
if budget_against_field == "Project":
budget_against = "_Test Project"
@@ -167,7 +194,8 @@ def make_budget(budget_against=None, cost_center=None):
if budget_against == "Project":
budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", "_Test Project/_Test Fiscal Year 2013%")})
else:
budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", "_Test Cost Center - _TC/_Test Fiscal Year 2013%")})
cost_center_name = "{0}%".format(cost_center or "_Test Cost Center - _TC/_Test Fiscal Year 2013")
budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", cost_center_name)})
for d in budget_list:
frappe.db.sql("delete from `tabBudget` where name = %(name)s", d)
frappe.db.sql("delete from `tabBudget Account` where parent = %(name)s", d)

View File

@@ -291,37 +291,39 @@ frappe.ui.form.on('Payment Entry', {
set_account_currency_and_balance: function(frm, account, currency_field,
balance_field, callback_function) {
frappe.call({
method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_account_details",
args: {
"account": account,
"date": frm.doc.posting_date
},
callback: function(r, rt) {
if(r.message) {
frm.set_value(currency_field, r.message['account_currency']);
frm.set_value(balance_field, r.message['account_balance']);
if (frm.doc.posting_date) {
frappe.call({
method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_account_details",
args: {
"account": account,
"date": frm.doc.posting_date
},
callback: function(r, rt) {
if(r.message) {
frm.set_value(currency_field, r.message['account_currency']);
frm.set_value(balance_field, r.message['account_balance']);
if(frm.doc.payment_type=="Receive" && currency_field=="paid_to_account_currency") {
frm.toggle_reqd(["reference_no", "reference_date"],
(r.message['account_type'] == "Bank" ? 1 : 0));
if(!frm.doc.received_amount && frm.doc.paid_amount)
frm.events.paid_amount(frm);
} else if(frm.doc.payment_type=="Pay" && currency_field=="paid_from_account_currency") {
frm.toggle_reqd(["reference_no", "reference_date"],
(r.message['account_type'] == "Bank" ? 1 : 0));
if(frm.doc.payment_type=="Receive" && currency_field=="paid_to_account_currency") {
frm.toggle_reqd(["reference_no", "reference_date"],
(r.message['account_type'] == "Bank" ? 1 : 0));
if(!frm.doc.received_amount && frm.doc.paid_amount)
frm.events.paid_amount(frm);
} else if(frm.doc.payment_type=="Pay" && currency_field=="paid_from_account_currency") {
frm.toggle_reqd(["reference_no", "reference_date"],
(r.message['account_type'] == "Bank" ? 1 : 0));
if(!frm.doc.paid_amount && frm.doc.received_amount)
frm.events.received_amount(frm);
if(!frm.doc.paid_amount && frm.doc.received_amount)
frm.events.received_amount(frm);
}
if(callback_function) callback_function(frm);
frm.events.hide_unhide_fields(frm);
frm.events.set_dynamic_labels(frm);
}
if(callback_function) callback_function(frm);
frm.events.hide_unhide_fields(frm);
frm.events.set_dynamic_labels(frm);
}
}
});
});
}
},
paid_from_account_currency: function(frm) {

View File

@@ -492,9 +492,13 @@ def get_outstanding_reference_documents(args):
for d in outstanding_invoices:
d["exchange_rate"] = 1
if party_account_currency != company_currency \
and d.voucher_type in ("Sales Invoice", "Purchase Invoice"):
if party_account_currency != company_currency:
if d.voucher_type in ("Sales Invoice", "Purchase Invoice"):
d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate")
elif d.voucher_type == "Journal Entry":
d["exchange_rate"] = get_exchange_rate(
party_account_currency, company_currency, d.posting_date
)
# Get all SO / PO which are not fully billed or aginst which full advance not paid
orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"), args.get("party"),

View File

@@ -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": [
{

View File

@@ -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,12 +343,12 @@ 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']);
@@ -483,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{

View File

@@ -12,6 +12,7 @@
"doctype": "DocType",
"document_type": "",
"editable_grid": 0,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
@@ -190,7 +191,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 +201,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 +223,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 +284,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 +315,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 +480,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 +845,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,
@@ -949,13 +981,13 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Company Address",
"label": "Company Address Name",
"length": 0,
"no_copy": 0,
"options": "Address",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
@@ -971,24 +1003,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 +1510,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 +1913,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": "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 +1950,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 +1960,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 +2572,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 +3045,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,
@@ -3597,7 +3658,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,
@@ -4450,7 +4511,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,
@@ -4511,7 +4572,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,
@@ -4542,7 +4603,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,
@@ -4627,10 +4688,11 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-06-22 14:45:35.257640",
"modified": "2017-07-07 13:05:37.469682",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
"name_case": "Title Case",
"owner": "Administrator",
"permissions": [
{

View File

@@ -132,8 +132,7 @@ class SalesInvoice(SellingController):
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
self.check_credit_limit()
if self.update_stock:
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()
@@ -804,9 +803,10 @@ class SalesInvoice(SellingController):
continue
for serial_no in item.serial_no.split("\n"):
sno = frappe.get_doc('Serial No', serial_no)
sno.sales_invoice = invoice
sno.db_update()
if serial_no and frappe.db.exists('Serial No', serial_no):
sno = frappe.get_doc('Serial No', serial_no)
sno.sales_invoice = invoice
sno.db_update()
def validate_serial_numbers(self):
"""

View File

@@ -1105,6 +1105,32 @@ 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("items", {
"item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC",
"qty": 100,
"rate": 50,
"income_account": "Sales - _TC",
"expense_account": "Cost of Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC"
})
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><th class="text-right" style="min-width: 80px;">Taxable Amount</th><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 10,000.00</td><td class="text-right">(10.0%) \u20b9 1,000.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)

View File

@@ -11,6 +11,7 @@
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
@@ -1423,7 +1424,7 @@
"collapsible": 1,
"collapsible_depends_on": "eval:doc.serial_no || doc.batch_no",
"columns": 0,
"depends_on": "",
"depends_on": "eval: parent.update_stock",
"fieldname": "warehouse_and_reference",
"fieldtype": "Section Break",
"hidden": 0,
@@ -2165,7 +2166,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-07-03 19:34:14.820285",
"modified": "2017-07-06 17:54:03.347700",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",

View File

@@ -1,9 +1,3 @@
{% var letterhead= filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) || frappe.defaults.get_default("letter_head"); %}
{% if(letterhead) { %}
<div style="margin-bottom: 7px;" class="text-center">
{%= frappe.boot.letter_heads[letterhead].header %}
</div>
{% } %}
<h2 class="text-center">{%= __(report.report_name) %}</h2>
<h4 class="text-center">{%= filters.customer || filters.supplier %} </h4>
<h5 class="text-center">

View File

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

View File

@@ -1,9 +1,3 @@
{% var letterhead= filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) || frappe.defaults.get_default("letter_head"); %}
{% if(letterhead) { %}
<div style="margin-bottom: 7px;" class="text-center">
{%= frappe.boot.letter_heads[letterhead].header %}
</div>
{% } %}
<h2 class="text-center">{%= __("Statement of Account") %}</h2>
<h4 class="text-center">
{% if (filters.party_name) { %}

View File

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

View File

@@ -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],
}

View File

@@ -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",
}]

View 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.

View File

@@ -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"
},
]
},
{

View File

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

View File

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

View File

@@ -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,110 @@ 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, taxable_amount = self.get_distinct_items()
rows = get_table_rows(distinct_items, item_tax, tax_accounts, company_currency, taxable_amount)
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": "".join(headings),
"rows": "".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 = []
taxable_amount = {}
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)
taxable_amount[item_code] = item.net_amount
else:
taxable_amount[item_code] = taxable_amount.get(item_code, 0) + item.net_amount
return distinct_items, taxable_amount
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, taxable_amount):
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>")
item_code = item.item_code or item.item_name
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(taxable_amount.get(item_code, 0), item.precision("net_amount"), company_currency),
"taxes": "".join(taxes)
}))
return rows

View 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": "&lt; 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"), [])

View File

@@ -56,7 +56,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Series",
"length": 0,
@@ -88,7 +88,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Opportunity From",
"length": 0,
@@ -277,8 +277,8 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Opportunity Type",
"length": 0,
"no_copy": 0,
@@ -310,7 +310,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Status",
"length": 0,
@@ -1189,7 +1189,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-06-13 14:29:07.077697",
"modified": "2017-07-10 15:29:23.921967",
"modified_by": "Administrator",
"module": "CRM",
"name": "Opportunity",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

View File

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 KiB

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 173 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -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> &lt;one line to give the program's name and a brief idea of what it does.&gt;
Copyright (C) &lt;year&gt; &lt;name of author&gt;
<pre><code> &lt;one line="" to="" give="" the="" program's="" name="" and="" a="" brief="" idea="" of="" what="" it="" does.=""&gt;
Copyright (C) &lt;year&gt; &lt;name of="" author=""&gt;
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

View File

@@ -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.
![Eröffnungsbuchung]({{docs_base_url}}/assets/old_images/erpnext/opening-entry-1.png)
<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:
![Eröffnungsbuchung]({{docs_base_url}}/assets/old_images/erpnext/image-temp-opening.png)
![Eröffnungsbuchung]({{docs_base_url}}/assets/old_images/erpnext/opening-entry-2.png)
<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:
![Probebilanz]({{docs_base_url}}/assets/old_images/erpnext/trial-balance-1.png)
<img class="screenshot" alt="Probebilanz" src="{{docs_base_url}}/assets/img/accounts/opening-4.png">
### Offene Rechnungen

View File

@@ -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.
![]({{docs_base_url}}/assets/old_images/erpnext/overview.png)
<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._

View File

@@ -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.
![Fremdvergabe]({{docs_base_url}}/assets/old_images/erpnext/subcontract.png)
<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.

View File

@@ -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.
![Lagerabgleichsdaten]({{docs_base_url}}/assets/old_images/erpnext/stock-reco-data.png)
<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
![Bestandsabgleich Überprüfung]({{docs_base_url}}/assets/old_images/erpnext/stock-reco-upload.png)
<img class="screenshot" alt="Bestandsabgleich Überprüfung" src="{{docs_base_url}}/assets/img/setup/stock-reco-upload.gif">
### Bericht zum Lagerbuch
![Bestandsabgleich Hauptbuch]({{docs_base_url}}/assets/old_images/erpnext/stock-reco-ledger.png)
<img class="screenshot" alt="Bestandsabgleich" src="{{docs_base_url}}/assets/img/setup/stock-reco-ledger.png">
##### So arbeitet der Bestandsabgleich

View File

@@ -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.
![Workflow]({{docs_base_url}}/assets/old_images/erpnext/workflow-leave-fl.jpg)
<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".
![Workflow Mitarbeiter LA]({{docs_base_url}}/assets/old_images/erpnext/workflow-employee-la.png)
<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.
![Urlaubsgenehmiger]({{docs_base_url}}/assets/old_images/erpnext/workflow-hr-user-la.png)
<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.
![Workflow Urlaubsgenehmiger]({{docs_base_url}}/assets/old_images/erpnext/workflow-leave-approver-la.png)
<img class="screenshot" alt="Workflow" src="{{docs_base_url}}/assets/img/setup/workflow-5.png">
{next}

View File

@@ -6,7 +6,7 @@ functions. This diagram does not cover all the features of ERPNext.
![]({{docs_base_url}}/assets/old_images/erpnext/overview.png)
[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._

View File

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

View File

@@ -14,6 +14,13 @@ 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
@@ -54,6 +61,12 @@ For **Sales 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.

View File

@@ -91,5 +91,5 @@ To setup Email Digests, go to:
#### Figure 4: Set up Email Digest
![Email Digest]({{docs_base_url}}/assets/old_images/erpnext/email-digest.png)
<img class="screenshot" alt="Email Digest" src="{{docs_base_url}}/assets/img/setup/email/email-digest.png">

View File

@@ -42,6 +42,7 @@ Email alerts allow you to set conditions according to the field data in your doc
The above example will send an Email Alert when a Task is saved with the status "Open" and the Expected End Date for the Task is the date on or before the date on which it was saved on.
### Setting a Message
You can use both Jinja Tags (`{% raw %}{{ doc.[field_name] }}{% endraw %}`) and HTML tags in the message textbox.
@@ -64,6 +65,17 @@ You can use both Jinja Tags (`{% raw %}{{ doc.[field_name] }}{% endraw %}`) and
---
### Setting a Value after the Alert is Set
Sometimes to make sure that the email alert is not sent multiple times, you can
define a custom property (via Customize Form) like "Email Alert Sent" and then
set this property after the alert is sent by setting the **Set Property After Alert**
field.
Then you can use that as a condition in the **Condition** rules to ensure emails are not sent multiple times
<img class="screenshot" alt="Setting Property in Email Alert" src="{{docs_base_url}}/assets/img/setup/email/email-alert-subject.png">
### Example
1. Defining the Criteria

View File

@@ -31,7 +31,7 @@ A predefined template of an spreadsheet file should be followed for importing it
#### Step 2: Enter Data in csv file.
![Stock Reco Data]({{docs_base_url}}/assets/old_images/erpnext/stock-reco-data.png)
<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
![Stock Reco Review]({{docs_base_url}}/assets/old_images/erpnext/stock-reco-upload.png)
<img class="screenshot" alt="Stock Reconciliation" src="{{docs_base_url}}/assets/img/setup/stock-reco-upload.gif">
### Stock Ledger Report
![Stock Reco Ledger]({{docs_base_url}}/assets/old_images/erpnext/stock-reco-ledger.png)
<img class="screenshot" alt="Stock Reconciliation" src="{{docs_base_url}}/assets/img/setup//stock-reco-ledger.png">
**How Stock Reconciliation Works**

View File

@@ -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.
![Workflow]({{docs_base_url}}/assets/old_images/erpnext/workflow-leave-fl.jpg)
<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"
![Workflow Employee LA]({{docs_base_url}}/assets/img/setup/workflow-3.png)
<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.
![Leave Approver]({{docs_base_url}}/assets/img/setup/workflow-4.png)
<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.
![Workflow Leave Approver]({{docs_base_url}}/assets/img/setup/workflow-5.png)
<img class="screenshot" alt="Workflow" src="{{docs_base_url}}/assets/img/setup/workflow-5.png">
{next}

View File

@@ -37,9 +37,9 @@ These numbers help to track individual units or batches of Items which you sell.
### 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
@@ -55,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.
@@ -79,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)">

View File

@@ -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. Its 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">

View File

@@ -80,6 +80,13 @@ website_route_rules = [
"parents": [{"title": _("Supplier Quotation"), "name": "quotations"}]
}
},
{"from_route": "/quotes", "to_route": "Quotation"},
{"from_route": "/quotes/<path:name>", "to_route": "order",
"defaults": {
"doctype": "Quotation",
"parents": [{"title": _("Quotes"), "name": "quotes"}]
}
},
{"from_route": "/shipments", "to_route": "Delivery Note"},
{"from_route": "/shipments/<path:name>", "to_route": "order",
"defaults": {
@@ -110,6 +117,7 @@ standard_portal_menu_items = [
{"title": _("Projects"), "route": "/project", "reference_doctype": "Project"},
{"title": _("Request for Quotations"), "route": "/rfq", "reference_doctype": "Request for Quotation", "role": "Supplier"},
{"title": _("Supplier Quotation"), "route": "/quotations", "reference_doctype": "Supplier Quotation", "role": "Supplier"},
{"title": _("Quotes"), "route": "/quotes", "reference_doctype": "Quotation", "role":"Customer"},
{"title": _("Orders"), "route": "/orders", "reference_doctype": "Sales Order", "role":"Customer"},
{"title": _("Invoices"), "route": "/invoices", "reference_doctype": "Sales Invoice", "role":"Customer"},
{"title": _("Shipments"), "route": "/shipments", "reference_doctype": "Delivery Note", "role":"Customer"},

View File

@@ -12,32 +12,31 @@ import frappe.utils
class TestDailyWorkSummary(unittest.TestCase):
def test_email_trigger(self):
settings, employees, emails = self.setup_and_prepare_test()
for d in employees:
self.setup_and_prepare_test()
for d in self.employees:
# check that email is sent to this employee
self.assertTrue(d.user_id in [d.recipient for d in emails
if settings.subject in d.message])
self.assertTrue(d.user_id in [d.recipient for d in self.emails
if self.settings.subject in d.message])
def test_email_trigger_failed(self):
hour = '00'
if frappe.utils.nowtime().split(':')[0]=='00':
hour = '01'
settings, employees, emails = self.setup_and_prepare_test(hour)
self.setup_and_prepare_test(hour)
for d in employees:
for d in self.employees:
# check that email is sent to this employee
self.assertFalse(d.user_id in [d.recipient for d in emails
if settings.subject in d.message])
self.assertFalse(d.user_id in [d.recipient for d in self.emails
if self.settings.subject in d.message])
def test_incoming(self):
settings, employees, emails = self.setup_and_prepare_test()
# get test mail with message-id as in-reply-to
self.setup_and_prepare_test()
with open(os.path.join(os.path.dirname(__file__), "test_data", "test-reply.raw"), "r") as f:
test_mails = [f.read().replace('{{ sender }}', employees[-1].user_id)\
.replace('{{ message_id }}', emails[-1].message_id)]
test_mails = [f.read().replace('{{ sender }}', self.employees[-1].user_id)\
.replace('{{ message_id }}', self.emails[-1].message_id)]
# pull the mail
email_account = frappe.get_doc("Email Account", "_Test Email Account 1")
@@ -52,30 +51,34 @@ class TestDailyWorkSummary(unittest.TestCase):
self.assertTrue('I built Daily Work Summary!' in summary)
def setup_and_prepare_test(self, hour=None):
if not hour:
hour = frappe.utils.nowtime().split(':')[0]
frappe.db.sql('delete from `tabDaily Work Summary`')
frappe.db.sql('delete from `tabEmail Queue`')
frappe.db.sql('delete from `tabEmail Queue Recipient`')
frappe.db.sql('delete from `tabCommunication`')
# setup email to trigger at this our
settings = frappe.get_doc('Daily Work Summary Settings')
settings.companies = []
settings.append('companies', dict(company='_Test Company',
send_emails_at=hour + ':00'))
settings.test_subject = 'this is a subject for testing summary emails'
settings.save()
self.setup_settings(hour)
from erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings \
import trigger_emails
trigger_emails()
# check if emails are created
employees = frappe.get_all('Employee', fields = ['user_id'],
filters=dict(company='_Test Company', status='Active'))
self.employees = frappe.get_all('Employee', fields = ['user_id'],
filters=dict(company='_Test Company', status='Active', user_id=('!=', 'test@example.com')))
emails = frappe.db.sql("""select r.recipient, q.message, q.message_id from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r where q.name = r.parent""", as_dict=1)
self.emails = frappe.db.sql("""select r.recipient, q.message, q.message_id from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r where q.name = r.parent""", as_dict=1)
frappe.db.commit()
def setup_settings(self, hour=None):
# setup email to trigger at this our
if not hour:
hour = frappe.utils.nowtime().split(':')[0]
self.settings = frappe.get_doc('Daily Work Summary Settings')
self.settings.companies = []
self.settings.append('companies', dict(company='_Test Company',
send_emails_at=hour + ':00'))
self.settings.test_subject = 'this is a subject for testing summary emails'
self.settings.save()
return settings, employees, emails

View File

@@ -5,12 +5,24 @@ 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.add_fetch('buying_price_list', 'currency', 'currency')
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) {

View File

@@ -365,9 +365,6 @@ class BOM(WebsiteGenerator):
base_total_rm_cost = 0
for d in self.get('items'):
if d.bom_no:
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.stock_qty, self.precision("stock_qty", d))
d.base_amount = d.amount * flt(self.conversion_rate)
@@ -408,6 +405,7 @@ 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,
'image' : d.image,
'stock_uom' : d.stock_uom,
@@ -427,7 +425,8 @@ class BOM(WebsiteGenerator):
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,
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
@@ -437,9 +436,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'],
'stock_qty' : d['qty_consumed_per_unit']*stock_qty,
'stock_qty' : d['qty_consumed_per_unit'] * stock_qty,
'rate' : flt(d['rate']),
}))
@@ -493,6 +493,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
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
@@ -501,18 +502,20 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
and bom_item.parent = bom.name
and item.name = bom_item.item_code
and is_stock_item = 1
{conditions}
group by item_code, stock_uom"""
{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:

View File

@@ -8,7 +8,8 @@
"parentfield": "items",
"stock_qty": 1.0,
"rate": 5000.0,
"stock_uom": "_Test UOM"
"stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC"
},
{
"amount": 2000.0,
@@ -17,7 +18,8 @@
"parentfield": "items",
"stock_qty": 2.0,
"rate": 1000.0,
"stock_uom": "_Test UOM"
"stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC"
}
],
"docstatus": 1,
@@ -48,7 +50,8 @@
"parentfield": "items",
"stock_qty": 1.0,
"rate": 5000.0,
"stock_uom": "_Test UOM"
"stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC"
},
{
"amount": 2000.0,
@@ -57,7 +60,8 @@
"parentfield": "items",
"stock_qty": 2.0,
"rate": 1000.0,
"stock_uom": "_Test UOM"
"stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC"
}
],
"docstatus": 1,
@@ -86,7 +90,8 @@
"parentfield": "items",
"stock_qty": 1.0,
"rate": 5000.0,
"stock_uom": "_Test UOM"
"stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC"
},
{
"amount": 2000.0,
@@ -96,7 +101,8 @@
"parentfield": "items",
"stock_qty": 3.0,
"rate": 1000.0,
"stock_uom": "_Test UOM"
"stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC"
}
],
"docstatus": 1,
@@ -126,7 +132,8 @@
"parentfield": "items",
"stock_qty": 2.0,
"rate": 3000.0,
"stock_uom": "_Test UOM"
"stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC"
}
],
"docstatus": 1,

View File

@@ -7,9 +7,9 @@
"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": [
@@ -104,6 +104,37 @@
"set_only_once": 0,
"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,
@@ -481,7 +512,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-06-02 19:29:34.498719",
"modified": "2017-07-04 17:51:18.151002",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Explosion Item",

View File

@@ -22,7 +22,7 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
@@ -113,7 +113,7 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
@@ -136,6 +136,37 @@
"unique": 0,
"width": "150px"
},
{
"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": 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,
@@ -729,7 +760,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-05-23 15:59:37.946963",
"modified": "2017-07-04 17:42:37.218408",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Item",

View File

@@ -327,7 +327,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-05-23 16:04:32.442287",
"modified": "2017-07-04 16:04:32.442287",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Scrap Item",

View File

@@ -7,7 +7,72 @@ frappe.ui.form.on("Production Order", {
'Timesheet': 'Make Timesheet',
'Stock Entry': 'Make Stock Entry',
}
// Set query for warehouses
frm.set_query("wip_warehouse", function(doc) {
return {
filters: {
'company': frm.doc.company,
}
}
});
frm.set_query("source_warehouse", "required_items", function() {
return {
filters: {
'company': frm.doc.company,
}
}
});
frm.set_query("fg_warehouse", function() {
return {
filters: {
'company': frm.doc.company,
'is_group': 0
}
}
});
frm.set_query("scrap_warehouse", function() {
return {
filters: {
'company': frm.doc.company,
'is_group': 0
}
}
});
// Set query for BOM
frm.set_query("bom_no", function() {
if (frm.doc.production_item) {
return{
query: "erpnext.controllers.queries.bom",
filters: {item: cstr(frm.doc.production_item)}
}
} else msgprint(__("Please enter Production Item first"));
});
// Set query for FG Item
frm.set_query("production_item", function() {
return {
query: "erpnext.controllers.queries.item_query",
filters:{
'is_stock_item': 1,
}
}
});
// Set query for FG Item
frm.set_query("project", function() {
return{
filters:[
['Project', 'status', 'not in', 'Completed, Cancelled']
]
}
});
},
onload: function(frm) {
if (!frm.doc.status)
frm.doc.status = 'Draft';
@@ -25,12 +90,9 @@ frappe.ui.form.on("Production Order", {
// formatter for production order operation
frm.set_indicator_formatter('operation',
function(doc) { return (frm.doc.qty==doc.completed_qty) ? "green" : "orange" })
erpnext.production_order.set_custom_buttons(frm);
erpnext.production_order.setup_company_filter(frm);
erpnext.production_order.setup_bom_filter(frm);
function(doc) { return (frm.doc.qty==doc.completed_qty) ? "green" : "orange" });
},
refresh: function(frm) {
erpnext.toggle_naming_series();
erpnext.production_order.set_custom_buttons(frm);
@@ -53,6 +115,7 @@ frappe.ui.form.on("Production Order", {
})
}
},
show_progress: function(frm) {
var bars = [];
var message = '';
@@ -85,10 +148,85 @@ frappe.ui.form.on("Production Order", {
}
}
frm.dashboard.add_progress(__('Status'), bars, message);
},
production_item: function(frm) {
if (frm.doc.production_item) {
frappe.call({
method: "erpnext.manufacturing.doctype.production_order.production_order.get_item_details",
args: {
item: frm.doc.production_item,
project: frm.doc.project
},
callback: function(r) {
if(r.message) {
erpnext.in_production_item_onchange = true;
$.each(["description", "stock_uom", "project", "bom_no"], function(i, field) {
frm.set_value(field, r.message[field]);
});
if(r.message["set_scrap_wh_mandatory"]){
frm.toggle_reqd("scrap_warehouse", true);
}
erpnext.in_production_item_onchange = false;
}
}
});
}
},
project: function(frm) {
if(!erpnext.in_production_item_onchange) {
frm.trigger("production_item");
}
},
bom_no: function(frm) {
return frm.call({
doc: frm.doc,
method: "get_items_and_operations_from_bom",
callback: function(r) {
if(r.message["set_scrap_wh_mandatory"]){
frm.toggle_reqd("scrap_warehouse", true);
}
}
});
},
use_multi_level_bom: function(frm) {
if(frm.doc.bom_no) {
frm.trigger("bom_no");
}
},
qty: function(frm) {
frm.trigger('bom_no');
},
before_submit: function(frm) {
frm.toggle_reqd(["fg_warehouse", "wip_warehouse"], true);
frm.fields_dict.required_items.grid.toggle_reqd("source_warehouse", true);
}
});
frappe.ui.form.on("Production Order Item", {
source_warehouse: function(frm, cdt, cdn) {
var row = locals[cdt][cdn];
if(row.source_warehouse) {
frappe.call({
"method": "erpnext.stock.utils.get_latest_stock_qty",
args: {
item_code: row.item_code,
warehouse: row.source_warehouse
},
callback: function (r) {
frappe.model.set_value(row.doctype, row.name,
"available_qty_at_source_warehouse", r.message);
}
})
}
}
})
frappe.ui.form.on("Production Order Operation", {
workstation: function(frm, cdt, cdn) {
@@ -119,38 +257,46 @@ erpnext.production_order = {
var doc = frm.doc;
if (doc.docstatus === 1) {
if (doc.status != 'Stopped' && doc.status != 'Completed') {
frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Production Order'], __("Status"));
frm.add_custom_button(__('Stop'), function() {
erpnext.production_order.stop_production_order(frm, "Stopped");
}, __("Status"));
} else if (doc.status == 'Stopped') {
frm.add_custom_button(__('Re-open'), cur_frm.cscript['Unstop Production Order'], __("Status"));
frm.add_custom_button(__('Re-open'), function() {
erpnext.production_order.stop_production_order(frm, "Resumed");
}, __("Status"));
}
if(!frm.doc.skip_transfer){
if ((flt(doc.material_transferred_for_manufacturing) < flt(doc.qty)) && frm.doc.status != 'Stopped') {
if ((flt(doc.material_transferred_for_manufacturing) < flt(doc.qty))
&& frm.doc.status != 'Stopped') {
frm.has_start_btn = true;
var btn = frm.add_custom_button(__('Start'),
cur_frm.cscript['Transfer Raw Materials']);
btn.addClass('btn-primary');
var start_btn = frm.add_custom_button(__('Start'), function() {
erpnext.production_order.make_se(frm, 'Material Transfer for Manufacture');
});
start_btn.addClass('btn-primary');
}
}
if(!frm.doc.skip_transfer){
if ((flt(doc.produced_qty) < flt(doc.material_transferred_for_manufacturing)) && frm.doc.status != 'Stopped') {
if ((flt(doc.produced_qty) < flt(doc.material_transferred_for_manufacturing))
&& frm.doc.status != 'Stopped') {
frm.has_finish_btn = true;
var btn = frm.add_custom_button(__('Finish'),
cur_frm.cscript['Update Finished Goods']);
var finish_btn = frm.add_custom_button(__('Finish'), function() {
erpnext.production_order.make_se(frm, 'Manufacture');
});
if(doc.material_transferred_for_manufacturing==doc.qty) {
// all materials transferred for manufacturing,
// make this primary
btn.addClass('btn-primary');
// all materials transferred for manufacturing, make this primary
finish_btn.addClass('btn-primary');
}
}
} else {
if ((flt(doc.produced_qty) < flt(doc.qty)) && frm.doc.status != 'Stopped') {
frm.has_finish_btn = true;
var btn = frm.add_custom_button(__('Finish'),
cur_frm.cscript['Update Finished Goods']);
btn.addClass('btn-primary');
var finish_btn = frm.add_custom_button(__('Finish'), function() {
erpnext.production_order.make_se(frm, 'Manufacture');
});
finish_btn.addClass('btn-primary');
}
}
}
@@ -162,8 +308,8 @@ erpnext.production_order = {
doc.planned_operating_cost = 0.0;
for(var i=0;i<op.length;i++) {
var planned_operating_cost = flt(flt(op[i].hour_rate) * flt(op[i].time_in_mins) / 60, 2);
frappe.model.set_value('Production Order Operation',op[i].name, "planned_operating_cost", planned_operating_cost);
frappe.model.set_value('Production Order Operation', op[i].name,
"planned_operating_cost", planned_operating_cost);
doc.planned_operating_cost += planned_operating_cost;
}
refresh_field('planned_operating_cost');
@@ -176,37 +322,10 @@ erpnext.production_order = {
frm.set_value("total_operating_cost", (flt(frm.doc.additional_operating_cost) + variable_cost))
},
setup_company_filter: function(frm) {
var company_filter = function(doc) {
return {
filters: {
'company': frm.doc.company,
'is_group': 0
}
}
}
frm.fields_dict.source_warehouse.get_query = company_filter;
frm.fields_dict.fg_warehouse.get_query = company_filter;
frm.fields_dict.wip_warehouse.get_query = company_filter;
},
setup_bom_filter: function(frm) {
frm.set_query("bom_no", function(doc) {
if (doc.production_item) {
return{
query: "erpnext.controllers.queries.bom",
filters: {item: cstr(doc.production_item)}
}
} else frappe.msgprint(__("Please enter Production Item first"));
});
},
set_default_warehouse: function(frm) {
if (!(frm.doc.wip_warehouse || frm.doc.fg_warehouse)) {
frappe.call({
method: "erpnext.manufacturing.doctype.production_order.production_order.get_default_warehouse",
callback: function(r) {
if(!r.exe) {
frm.set_value("wip_warehouse", r.message.wip_warehouse);
@@ -215,45 +334,15 @@ erpnext.production_order = {
}
});
}
}
}
$.extend(cur_frm.cscript, {
before_submit: function() {
cur_frm.toggle_reqd(["fg_warehouse", "wip_warehouse"], true);
},
production_item: function(doc) {
frappe.call({
method: "erpnext.manufacturing.doctype.production_order.production_order.get_item_details",
args: {
item: doc.production_item,
project: doc.project
},
callback: function(r) {
$.each(["description", "stock_uom", "project", "bom_no"], function(i, field) {
cur_frm.set_value(field, r.message[field]);
});
if(r.message["set_scrap_wh_mandatory"]){
cur_frm.toggle_reqd("scrap_warehouse", true);
}
}
});
},
project: function(doc) {
cur_frm.cscript.production_item(doc)
},
make_se: function(purpose) {
var me = this;
if(!this.frm.doc.skip_transfer){
make_se: function(frm, purpose) {
if(!frm.doc.skip_transfer){
var max = (purpose === "Manufacture") ?
flt(this.frm.doc.material_transferred_for_manufacturing) - flt(this.frm.doc.produced_qty) :
flt(this.frm.doc.qty) - flt(this.frm.doc.material_transferred_for_manufacturing);
flt(frm.doc.material_transferred_for_manufacturing) - flt(frm.doc.produced_qty) :
flt(frm.doc.qty) - flt(frm.doc.material_transferred_for_manufacturing);
} else {
var max = flt(this.frm.doc.qty) - flt(this.frm.doc.produced_qty);
var max = flt(frm.doc.qty) - flt(frm.doc.produced_qty);
}
frappe.prompt({fieldtype:"Float", label: __("Qty for {0}", [purpose]), fieldname:"qty",
@@ -266,7 +355,7 @@ $.extend(cur_frm.cscript, {
frappe.call({
method:"erpnext.manufacturing.doctype.production_order.production_order.make_stock_entry",
args: {
"production_order_id": me.frm.doc.name,
"production_order_id": frm.doc.name,
"purpose": purpose,
"qty": data.qty
},
@@ -277,59 +366,20 @@ $.extend(cur_frm.cscript, {
});
}, __("Select Quantity"), __("Make"));
},
bom_no: function() {
return this.frm.call({
doc: this.frm.doc,
method: "set_production_order_operations",
stop_production_order: function(frm, status) {
frappe.call({
method: "erpnext.manufacturing.doctype.production_order.production_order.stop_unstop",
args: {
production_order: frm.doc.name,
status: status
},
callback: function(r) {
if(r.message["set_scrap_wh_mandatory"]){
cur_frm.toggle_reqd("scrap_warehouse", true);
if(r.message) {
frm.set_value("status", r.message);
frm.reload_doc();
}
}
});
},
use_multi_level_bom: function() {
if(this.frm.doc.bom_no) {
this.frm.trigger("bom_no");
}
},
qty: function() {
frappe.ui.form.trigger("Production Order", 'bom_no')
},
});
cur_frm.cscript['Stop Production Order'] = function() {
$c_obj(cur_frm.doc, 'stop_unstop', 'Stopped', function(r, rt) {cur_frm.refresh();});
}
cur_frm.cscript['Unstop Production Order'] = function() {
$c_obj(cur_frm.doc, 'stop_unstop', 'Unstopped', function(r, rt) {cur_frm.refresh();});
}
cur_frm.cscript['Transfer Raw Materials'] = function() {
cur_frm.cscript.make_se('Material Transfer for Manufacture');
}
cur_frm.cscript['Update Finished Goods'] = function() {
cur_frm.cscript.make_se('Manufacture');
}
cur_frm.fields_dict['production_item'].get_query = function(doc) {
return {
query: "erpnext.controllers.queries.item_query",
filters:{
'is_stock_item': 1,
}
})
}
}
cur_frm.fields_dict['project'].get_query = function(doc, dt, dn) {
return{
filters:[
['Project', 'status', 'not in', 'Completed, Cancelled']
]
}
}

View File

@@ -95,7 +95,7 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "\nDraft\nSubmitted\nNot Started\nStopped\nUnstopped\nIn Process\nCompleted\nCancelled",
"options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nCancelled",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@@ -139,38 +139,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "project",
"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": "Project",
"length": 0,
"no_copy": 0,
"oldfieldname": "project",
"oldfieldtype": "Link",
"options": "Project",
"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,
@@ -266,37 +234,6 @@
"unique": 0,
"width": "50%"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "",
"fieldname": "sales_order",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sales Order",
"length": 0,
"no_copy": 0,
"options": "Sales Order",
"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,
@@ -329,37 +266,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Check if material transfer entry is not required",
"fieldname": "skip_transfer",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Skip Material Transfer",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -427,6 +333,100 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "",
"fieldname": "sales_order",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sales Order",
"length": 0,
"no_copy": 0,
"options": "Sales Order",
"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,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "project",
"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": "Project",
"length": 0,
"no_copy": 0,
"oldfieldname": "project",
"oldfieldtype": "Link",
"options": "Project",
"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,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Check if material transfer entry is not required",
"fieldname": "skip_transfer",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Skip Material Transfer",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -457,38 +457,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "",
"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 (for reserving Items)",
"length": 0,
"no_copy": 0,
"options": "Warehouse",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -519,34 +487,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_12",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"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,
@@ -579,6 +519,34 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_12",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"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,
@@ -616,7 +584,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "time",
"fieldname": "required_items_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -625,10 +593,9 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Time",
"label": "Required Items",
"length": 0,
"no_copy": 0,
"options": "fa fa-time",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -647,9 +614,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "expected_delivery_date",
"fieldtype": "Date",
"fieldname": "required_items",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -657,10 +623,43 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Expected Delivery Date",
"label": "Required Items",
"length": 0,
"no_copy": 1,
"options": "Production Order Item",
"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,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "time",
"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": "Time",
"length": 0,
"no_copy": 0,
"options": "fa fa-time",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -708,7 +707,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "planned_end_date",
"fieldname": "actual_start_date",
"fieldtype": "Datetime",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -717,9 +716,9 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Planned End Date",
"label": "Actual Start Date",
"length": 0,
"no_copy": 1,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -767,7 +766,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "actual_start_date",
"fieldname": "planned_end_date",
"fieldtype": "Datetime",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -776,9 +775,9 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Actual Start Date",
"label": "Planned End Date",
"length": 0,
"no_copy": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -821,6 +820,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "expected_delivery_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": "Expected Delivery Date",
"length": 0,
"no_copy": 0,
"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,
@@ -1070,67 +1099,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"columns": 0,
"fieldname": "required_items_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Required Items",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "required_items",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Required Items",
"length": 0,
"no_copy": 1,
"options": "Production Order Item",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -1390,7 +1358,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-06-13 14:29:00.457874",
"modified": "2017-07-10 14:29:00.457874",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Order",

View File

@@ -3,24 +3,21 @@
from __future__ import unicode_literals
import frappe
import json
from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate
from frappe import _
from frappe.utils import time_diff_in_seconds
from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict
from dateutil.relativedelta import relativedelta
from erpnext.stock.doctype.item.item import validate_end_of_life
from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError, NotInWorkingHoursError
from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError
from erpnext.projects.doctype.timesheet.timesheet import OverlapError
from erpnext.stock.doctype.stock_entry.stock_entry import get_additional_costs
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
from erpnext.stock.utils import get_bin
from frappe.utils.csvutils import getlink
from erpnext.stock.utils import get_bin, validate_warehouse_company, get_latest_stock_qty
from erpnext.utilities.transaction_base import validate_uom_is_integer
class OverProductionError(frappe.ValidationError): pass
class StockOverProductionError(frappe.ValidationError): pass
@@ -38,15 +35,19 @@ class ProductionOrder(Document):
validate_bom_no(self.production_item, self.bom_no)
self.validate_sales_order()
self.validate_warehouse()
self.validate_warehouse_belongs_to_company()
self.calculate_operating_cost()
self.validate_qty()
self.validate_operation_time()
self.status = self.get_status()
from erpnext.utilities.transaction_base import validate_uom_is_integer
validate_uom_is_integer(self, "stock_uom", ["qty", "produced_qty"])
if not self.get("required_items"):
self.set_required_items()
else:
self.set_available_qty()
def validate_sales_order(self):
if self.sales_order:
so = frappe.db.sql("""select name, delivery_date, project from `tabSales Order`
@@ -64,11 +65,14 @@ class ProductionOrder(Document):
else:
frappe.throw(_("Sales Order {0} is not valid").format(self.sales_order))
def validate_warehouse(self):
from erpnext.stock.utils import validate_warehouse_company
def validate_warehouse_belongs_to_company(self):
warehouses = [self.fg_warehouse, self.wip_warehouse]
for d in self.get("required_items"):
if d.source_warehouse not in warehouses:
warehouses.append(d.source_warehouse)
for w in [self.source_warehouse, self.fg_warehouse, self.wip_warehouse]:
validate_warehouse_company(w, self.company)
for wh in warehouses:
validate_warehouse_company(wh, self.company)
def calculate_operating_cost(self):
self.planned_operating_cost, self.actual_operating_cost = 0.0, 0.0
@@ -79,7 +83,8 @@ class ProductionOrder(Document):
self.planned_operating_cost += flt(d.planned_operating_cost)
self.actual_operating_cost += flt(d.actual_operating_cost)
variable_cost = self.actual_operating_cost if self.actual_operating_cost else self.planned_operating_cost
variable_cost = self.actual_operating_cost if self.actual_operating_cost \
else self.planned_operating_cost
self.total_operating_cost = flt(self.additional_operating_cost) + flt(variable_cost)
def validate_production_order_against_so(self):
@@ -101,22 +106,16 @@ class ProductionOrder(Document):
# total qty in SO
so_qty = flt(so_item_qty) + flt(dnpi_qty)
allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings", "over_production_allowance_percentage"))
allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
"over_production_allowance_percentage"))
if total_qty > so_qty + (allowance_percentage/100 * so_qty):
frappe.throw(_("Cannot produce more Item {0} than Sales Order quantity {1}").format(self.production_item,
so_qty), OverProductionError)
def stop_unstop(self, status):
""" Called from client side on Stop/Unstop event"""
status = self.update_status(status)
self.update_planned_qty()
frappe.msgprint(_("Production Order status is {0}").format(status))
self.notify_update()
frappe.throw(_("Cannot produce more Item {0} than Sales Order quantity {1}")
.format(self.production_item, so_qty), OverProductionError)
def update_status(self, status=None):
'''Update status of production order if unknown'''
if not status:
if status != "Stopped":
status = self.get_status(status)
if status != self.status:
@@ -167,7 +166,6 @@ class ProductionOrder(Document):
self.db_set(fieldname, qty)
def before_submit(self):
self.set_required_items()
self.make_time_logs()
def on_submit(self):
@@ -184,10 +182,10 @@ class ProductionOrder(Document):
self.validate_cancel()
frappe.db.set(self,'status', 'Cancelled')
self.clear_required_items()
self.delete_timesheet()
self.update_completed_qty_in_material_request()
self.update_planned_qty()
self.update_reserved_qty_for_production()
def validate_cancel(self):
if self.status == "Stopped":
@@ -214,12 +212,11 @@ class ProductionOrder(Document):
def set_production_order_operations(self):
"""Fetch operations from BOM and set in 'Production Order'"""
self.set('operations', [])
if not self.bom_no \
or cint(frappe.db.get_single_value("Manufacturing Settings", "disable_capacity_planning")):
return
self.set('operations', [])
if self.use_multi_level_bom:
bom_list = frappe.get_doc("BOM", self.bom_no).traverse_tree()
@@ -240,8 +237,6 @@ class ProductionOrder(Document):
self.set('operations', operations)
self.calculate_time()
return check_if_scrap_warehouse_mandatory(self.bom_no)
def calculate_time(self):
bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity")
@@ -403,62 +398,60 @@ class ProductionOrder(Document):
update bin reserved_qty_for_production
called from Stock Entry for production, after submit, cancel
'''
if self.docstatus==1 and self.source_warehouse:
if self.material_transferred_for_manufacturing == self.produced_qty:
# clear required items table and save document
self.clear_required_items()
else:
# calculate transferred qty based on submitted
# stock entries
self.update_transaferred_qty_for_required_items()
if self.docstatus==1:
# calculate transferred qty based on submitted stock entries
self.update_transaferred_qty_for_required_items()
# update in bin
self.update_reserved_qty_for_production()
def clear_required_items(self):
'''Remove the required_items table and update the bins'''
items = [d.item_code for d in self.required_items]
self.required_items = []
self.update_child_table('required_items')
# completed, update reserved qty in bin
self.update_reserved_qty_for_production(items)
# update in bin
self.update_reserved_qty_for_production()
def update_reserved_qty_for_production(self, items=None):
'''update reserved_qty_for_production in bins'''
if not self.source_warehouse:
return
if not items:
items = [d.item_code for d in self.required_items]
for item in items:
stock_bin = get_bin(item, self.source_warehouse)
stock_bin.update_reserved_qty_for_production()
for d in self.required_items:
if d.source_warehouse:
stock_bin = get_bin(d.item_code, d.source_warehouse)
stock_bin.update_reserved_qty_for_production()
def get_items_and_operations_from_bom(self):
self.set_required_items()
self.set_production_order_operations()
return check_if_scrap_warehouse_mandatory(self.bom_no)
def set_available_qty(self):
for d in self.get("required_items"):
if d.source_warehouse:
d.available_qty_at_source_warehouse = get_latest_stock_qty(d.item_code, d.source_warehouse)
if self.wip_warehouse:
d.available_qty_at_wip_warehouse = get_latest_stock_qty(d.item_code, self.wip_warehouse)
def set_required_items(self):
'''set required_items for production to keep track of reserved qty'''
if self.source_warehouse:
self.required_items = []
if self.bom_no and self.qty:
item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=self.qty,
fetch_exploded = self.use_multi_level_bom)
for item in item_dict.values():
self.append('required_items', {'item_code': item.item_code,
'required_qty': item.qty})
#print frappe.as_json(self.required_items)
self.append('required_items', {
'item_code': item.item_code,
'required_qty': item.qty,
'source_warehouse': item.source_warehouse or item.default_warehouse
})
self.set_available_qty()
def update_transaferred_qty_for_required_items(self):
'''update transferred qty from submitted stock entries for that item against
the production order'''
for d in self.required_items:
transferred_qty = frappe.db.sql('''select count(qty)
transferred_qty = frappe.db.sql('''select sum(qty)
from `tabStock Entry` entry, `tabStock Entry Detail` detail
where
entry.production_order = %s
entry.purpose = "Material Transfer for Manufacture"
and entry.purpose = "Material Transfer for Manufacture"
and entry.docstatus = 1
and detail.parent = entry.name
and detail.item_code = %s''', (self.name, d.item_code))[0][0]
@@ -496,10 +489,12 @@ def get_item_details(item, project = None):
if not res["bom_no"]:
if project:
frappe.throw(_("Default BOM for {0} not found for Project {1}").format(item, project))
frappe.throw(_("Default BOM for {0} not found").format(item))
res = get_item_details(item)
frappe.msgprint(_("Default BOM not found for Item {0} and Project {1}").format(item, project))
else:
frappe.throw(_("Default BOM for {0} not found").format(item))
res['project'] = frappe.db.get_value('BOM', res['bom_no'], 'project')
res['project'] = project or frappe.db.get_value('BOM', res['bom_no'], 'project')
res.update(check_if_scrap_warehouse_mandatory(res["bom_no"]))
return res
@@ -507,16 +502,21 @@ def get_item_details(item, project = None):
@frappe.whitelist()
def check_if_scrap_warehouse_mandatory(bom_no):
res = {"set_scrap_wh_mandatory": False }
bom = frappe.get_doc("BOM", bom_no)
if bom_no:
bom = frappe.get_doc("BOM", bom_no)
if len(bom.scrap_items) > 0:
res["set_scrap_wh_mandatory"] = True
if len(bom.scrap_items) > 0:
res["set_scrap_wh_mandatory"] = True
return res
@frappe.whitelist()
def make_stock_entry(production_order_id, purpose, qty=None):
production_order = frappe.get_doc("Production Order", production_order_id)
if not frappe.db.get_value("Warehouse", production_order.wip_warehouse, "is_group"):
wip_warehouse = production_order.wip_warehouse
else:
wip_warehouse = None
stock_entry = frappe.new_doc("Stock Entry")
stock_entry.purpose = purpose
@@ -528,12 +528,10 @@ def make_stock_entry(production_order_id, purpose, qty=None):
stock_entry.fg_completed_qty = qty or (flt(production_order.qty) - flt(production_order.produced_qty))
if purpose=="Material Transfer for Manufacture":
if production_order.source_warehouse:
stock_entry.from_warehouse = production_order.source_warehouse
stock_entry.to_warehouse = production_order.wip_warehouse
stock_entry.to_warehouse = wip_warehouse
stock_entry.project = production_order.project
else:
stock_entry.from_warehouse = production_order.wip_warehouse
stock_entry.from_warehouse = wip_warehouse
stock_entry.to_warehouse = production_order.fg_warehouse
additional_costs = get_additional_costs(production_order, fg_qty=stock_entry.fg_completed_qty)
stock_entry.project = production_order.project
@@ -601,3 +599,18 @@ def make_new_timesheet(source_name, target_doc=None):
frappe.throw(_("Already completed"))
return ts
@frappe.whitelist()
def stop_unstop(production_order, status):
""" Called from client side on Stop/Unstop event"""
if not frappe.has_permission("Production Order", "write"):
frappe.throw(_("Not permitted"), frappe.PermissionError)
pro_order = frappe.get_doc("Production Order", production_order)
pro_order.update_status(status)
pro_order.update_planned_qty()
frappe.msgprint(_("Production Order has been {0}").format(status))
pro_order.notify_update()
return pro_order.status

View File

@@ -8,7 +8,7 @@ import frappe
from frappe.utils import flt, time_diff_in_hours, now, add_days, cint
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
from erpnext.manufacturing.doctype.production_order.production_order \
import make_stock_entry, ItemHasVariantError
import make_stock_entry, ItemHasVariantError, stop_unstop
from erpnext.stock.doctype.stock_entry import test_stock_entry
from erpnext.stock.doctype.item.test_item import get_total_projected_qty
from erpnext.stock.utils import get_bin
@@ -228,10 +228,46 @@ class TestProductionOrder(unittest.TestCase):
cint(bin1_on_start_production.reserved_qty_for_production))
self.assertEqual(cint(bin1_on_end_production.projected_qty),
cint(bin1_on_end_production.projected_qty))
def test_reserved_qty_for_stopped_production(self):
test_stock_entry.make_stock_entry(item_code="_Test Item",
target= self.warehouse, qty=100, basic_rate=100)
test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
target= self.warehouse, qty=100, basic_rate=100)
# required_items removed
self.pro_order.reload()
self.assertEqual(len(self.pro_order.required_items), 0)
# 0 0 0
self.test_reserved_qty_for_production_submit()
#2 0 -2
s = frappe.get_doc(make_stock_entry(self.pro_order.name,
"Material Transfer for Manufacture", 1))
s.submit()
#1 -1 0
bin1_on_start_production = get_bin(self.item, self.warehouse)
# reserved_qty_for_producion updated
self.assertEqual(cint(self.bin1_at_start.reserved_qty_for_production) + 1,
cint(bin1_on_start_production.reserved_qty_for_production))
# projected qty will now be 2 less (becuase of item movement)
self.assertEqual(cint(self.bin1_at_start.projected_qty),
cint(bin1_on_start_production.projected_qty) + 2)
# STOP
stop_unstop(self.pro_order.name, "Stopped")
bin1_on_stop_production = get_bin(self.item, self.warehouse)
# no change in reserved / projected
self.assertEqual(cint(bin1_on_stop_production.reserved_qty_for_production),
cint(self.bin1_at_start.reserved_qty_for_production))
self.assertEqual(cint(bin1_on_stop_production.projected_qty) + 1,
cint(self.bin1_at_start.projected_qty))
def test_scrap_material_qty(self):
prod_order = make_prod_order_test_record(planned_start_date=now(), qty=2)
@@ -286,10 +322,11 @@ def make_prod_order_test_record(**args):
pro_order.company = args.company or "_Test Company"
pro_order.stock_uom = args.stock_uom or "_Test UOM"
pro_order.use_multi_level_bom=0
pro_order.set_production_order_operations()
pro_order.get_items_and_operations_from_bom()
if args.source_warehouse:
pro_order.source_warehouse = args.source_warehouse
for item in pro_order.get("required_items"):
item.source_warehouse = args.source_warehouse
if args.planned_start_date:
pro_order.planned_start_date = args.planned_start_date

View File

@@ -13,6 +13,7 @@
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -43,6 +44,157 @@
"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": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"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": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Item Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description",
"fieldtype": "Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "qty_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Qty",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -72,6 +224,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -99,6 +252,95 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_9",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "available_qty_at_source_warehouse",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Available Qty at Source Warehouse",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "available_qty_at_wip_warehouse",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Available Qty at WIP Warehouse",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
@@ -111,7 +353,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-03-28 14:18:36.342161",
"modified": "2017-07-10 17:37:20.212361",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Order Item",

View File

@@ -13,6 +13,7 @@
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -42,6 +43,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -74,6 +76,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -85,7 +88,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "BOM",
"length": 0,
@@ -104,6 +107,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -135,6 +139,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -163,6 +168,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -193,6 +199,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -224,6 +231,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -256,6 +264,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -285,6 +294,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -314,6 +324,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -343,6 +354,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -371,6 +383,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -403,6 +416,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -434,6 +448,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -464,6 +479,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -493,6 +509,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -522,6 +539,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -552,6 +570,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -580,6 +599,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -610,6 +630,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -651,7 +672,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-03-27 15:56:29.010336",
"modified": "2017-05-29 18:02:04.252419",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Order Operation",

View File

@@ -406,8 +406,13 @@ erpnext.patches.v8_0.change_in_words_varchar_length
erpnext.patches.v8_0.update_stock_qty_value_in_bom_item
erpnext.patches.v8_0.create_domain_docs #16-05-2017
erpnext.patches.v8_0.update_sales_cost_in_project
erpnext.patches.v8_0.create_address_doc_from_address_field_in_company #10-05-2017
erpnext.patches.v8_0.save_system_settings
erpnext.patches.v8_1.delete_deprecated_reports
erpnext.patches.v8_1.setup_gst_india #2017-06-27
execute:frappe.reload_doc('regional', 'doctype', 'gst_hsn_code')
erpnext.patches.v8_1.removed_roles_from_gst_report_non_indian_account
erpnext.patches.v8_1.removed_roles_from_gst_report_non_indian_account
erpnext.patches.v8_1.gst_fixes #2017-07-06
erpnext.patches.v8_0.update_production_orders
erpnext.patches.v8_1.remove_sales_invoice_from_returned_serial_no
erpnext.patches.v8_1.allow_invoice_copy_to_edit_after_submit

View File

@@ -73,7 +73,7 @@ def validate_parent_account_for_warehouse(company=None):
if not company:
return
if cint(erpnext.is_perpetual_inventory_enabled(company)):
if cint(erpnext.is_perpetual_inventory_enabled(company.name)):
parent_account = frappe.db.sql("""select name from tabAccount
where account_type='Stock' and company=%s and is_group=1
and (warehouse is null or warehouse = '')""", company.name)

View File

@@ -0,0 +1,33 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
# new field address_html is created in place of address field for the company's address in PR #8754 (without patch)
# so here is the patch for moving the address details in the address doc
company_list = []
if 'address' in frappe.db.get_table_columns('Company'):
company_list = frappe.db.sql('''select name, address from `tabCompany`
where address is not null and address != ""''', as_dict=1)
for company in company_list:
add_list = company.address.split(" ")
if ',' in company.address:
add_list = company.address.rpartition(',')
elif ' ' in company.address:
add_list = company.address.rpartition(' ')
else:
add_list = [company.address, None, company.address]
doc = frappe.get_doc({
"doctype":"Address",
"address_line1": add_list[0],
"city": add_list[2],
"links": [{
"link_doctype": "Company",
"link_name": company.name
}]
})
doc.save()

View File

@@ -9,8 +9,8 @@ from frappe.model.mapper import get_mapped_doc
def execute():
# for converting student batch into student group
for doctype in ["Student Group", "Student Group Student", "Student Group Instructor", "Student Attendance"]:
frappe.reload_doc("schools", "doctype", doctype)
for doctype in ["Student Group", "Student Group Student", "Student Group Instructor", "Student Attendance", "Student"]:
frappe.reload_doc("schools", "doctype", frappe.scrub(doctype))
if frappe.db.table_exists("Student Batch"):
student_batches = frappe.db.sql('''select name as student_group_name, student_batch_name as batch,

View File

@@ -0,0 +1,46 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
# reload schema
for doctype in ("Production Order", "Production Order Item", "Production Order Operation",
"BOM Item", "BOM Explosion Item", "BOM"):
frappe.reload_doctype(doctype)
# fetch all draft and submitted production orders
fields = ["name"]
if "source_warehouse" in frappe.db.get_table_columns("Production Order"):
fields.append("source_warehouse")
pro_orders = frappe.get_all("Production Order", filters={"docstatus": ["!=", 2]}, fields=fields)
count = 0
for p in pro_orders:
pro_order = frappe.get_doc("Production Order", p.name)
count += 1
# set required items table
pro_order.set_required_items()
for item in pro_order.get("required_items"):
# set source warehouse based on parent
if not item.source_warehouse and "source_warehouse" in fields:
item.source_warehouse = pro_order.get("source_warehouse")
item.db_update()
if pro_order.docstatus == 1:
# update transferred qty based on Stock Entry, it also updates db
pro_order.update_transaferred_qty_for_required_items()
# Set status where it was 'Unstopped', as it is deprecated
if pro_order.status == "Unstopped":
status = pro_order.get_status()
pro_order.db_set("status", status)
elif pro_order.status == "Stopped":
pro_order.update_reserved_qty_for_production()
if count % 200 == 0:
frappe.db.commit()

View File

@@ -0,0 +1,12 @@
import frappe
def execute():
inv_copy_options = "ORIGINAL FOR RECIPIENT\nDUPLICATE FOR TRANSPORTER\nDUPLICATE FOR SUPPLIER\nTRIPLICATE FOR SUPPLIER"
frappe.db.sql("""update `tabCustom Field` set allow_on_submit=1, options=%s
where fieldname='invoice_copy' and dt = 'Sales Invoice'
""", inv_copy_options)
frappe.db.sql("""update `tabCustom Field` set read_only=1
where fieldname='gst_state_number' and dt = 'Address'
""")

View File

@@ -0,0 +1,58 @@
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
from erpnext.regional.india.setup import update_address_template
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
return
update_existing_custom_fields()
add_custom_fields()
update_address_template()
frappe.reload_doc("regional", "print_format", "gst_tax_invoice")
def update_existing_custom_fields():
frappe.db.sql("""update `tabCustom Field` set label = 'HSN/SAC'
where fieldname='gst_hsn_code' and label='GST HSN Code'
""")
frappe.db.sql("""update `tabCustom Field` set print_hide = 1
where fieldname in ('customer_gstin', 'supplier_gstin', 'company_gstin')
""")
frappe.db.sql("""update `tabCustom Field` set insert_after = 'address_display'
where fieldname in ('customer_gstin', 'supplier_gstin')
""")
frappe.db.sql("""update `tabCustom Field` set insert_after = 'company_address_display'
where fieldname = 'company_gstin'
""")
frappe.db.sql("""update `tabCustom Field` set insert_after = 'description'
where fieldname='gst_hsn_code' and dt in ('Sales Invoice Item', 'Purchase Invoice Item')
""")
def add_custom_fields():
hsn_sac_field = dict(fieldname='gst_hsn_code', label='HSN/SAC',
fieldtype='Data', options='item_code.gst_hsn_code', insert_after='description')
custom_fields = {
'Address': [
dict(fieldname='gst_state_number', label='GST State Number',
fieldtype='Int', insert_after='gst_state'),
],
'Sales Invoice': [
dict(fieldname='invoice_copy', label='Invoice Copy',
fieldtype='Select', insert_after='project', print_hide=1, allow_on_submit=1,
options='ORIGINAL FOR RECIPIENT\nDUPLICATE FOR TRANSPORTER\nTRIPLICATE FOR SUPPLIER'),
],
'Sales Order Item': [hsn_sac_field],
'Delivery Note Item': [hsn_sac_field],
'Purchase Order Item': [hsn_sac_field],
'Purchase Receipt Item': [hsn_sac_field]
}
for doctype, fields in custom_fields.items():
for df in fields:
create_custom_field(doctype, df)

View File

@@ -0,0 +1,18 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doctype("Serial No")
frappe.db.sql("""
update
`tabSerial No`
set
sales_invoice = NULL
where
sales_invoice in (select return_against from
`tabSales Invoice` where docstatus =1 and is_return=1)
and sales_invoice is not null and sales_invoice !='' """)

View File

@@ -5,6 +5,7 @@ def execute():
frappe.reload_doc('regional', 'doctype', 'gst_settings')
frappe.reload_doc('regional', 'doctype', 'gst_hsn_code')
frappe.reload_doc('stock', 'doctype', 'item')
frappe.reload_doc("stock", "doctype", "customs_tariff_number")
for report_name in ('GST Sales Register', 'GST Purchase Register',
'GST Itemised Sales Register', 'GST Itemised Purchase Register'):

View File

@@ -215,7 +215,7 @@ if(!erpnext.taxes.flags[cur_frm.cscript.tax_table]) {
erpnext.taxes.set_conditional_mandatory_rate_or_amount(open_form);
} else {
// apply in current row
erpnext.taxes.set_conditional_mandatory_rate_or_amount(frm.get_field('taxes').grid.get_grid_row(cdn));
erpnext.taxes.set_conditional_mandatory_rate_or_amount(frm.get_field('taxes').grid.get_row(cdn));
}
});

View File

@@ -634,5 +634,99 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
}
this.calculate_outstanding_amount(false)
},
show_item_wise_taxes: function() {
if(this.frm.fields_dict.other_charges_calculation) {
this.frm.toggle_display("other_charges_calculation", this.frm.doc.other_charges_calculation);
}
},
set_item_wise_tax_breakup: function() {
if(this.frm.fields_dict.other_charges_calculation) {
var html = this.get_item_wise_taxes_html();
// console.log(html);
this.frm.set_value("other_charges_calculation", html);
this.show_item_wise_taxes();
}
},
get_item_wise_taxes_html: function() {
var item_tax = {};
var tax_accounts = [];
var company_currency = this.get_company_currency();
$.each(this.frm.doc["taxes"] || [], function(i, tax) {
var tax_amount_precision = precision("tax_amount", tax);
var tax_rate_precision = precision("rate", tax);
$.each(JSON.parse(tax.item_wise_tax_detail || '{}'),
function(item_code, tax_data) {
if(!item_tax[item_code]) item_tax[item_code] = {};
if($.isArray(tax_data)) {
var tax_rate = "";
if(tax_data[0] != null) {
tax_rate = (tax.charge_type === "Actual") ?
format_currency(flt(tax_data[0], tax_amount_precision),
company_currency, tax_amount_precision) :
(flt(tax_data[0], tax_rate_precision) + "%");
}
var tax_amount = format_currency(flt(tax_data[1], tax_amount_precision),
company_currency, tax_amount_precision);
item_tax[item_code][tax.name] = [tax_rate, tax_amount];
} else {
item_tax[item_code][tax.name] = [flt(tax_data, tax_rate_precision) + "%", ""];
}
});
tax_accounts.push([tax.name, tax.account_head]);
});
var headings = $.map([__("Item Name"), __("Taxable Amount")].concat($.map(tax_accounts,
function(head) { return head[1]; })), function(head) {
if(head==__("Item Name")) {
return '<th style="min-width: 100px;" class="text-left">' + (head || "") + "</th>";
} else {
return '<th style="min-width: 80px;" class="text-right">' + (head || "") + "</th>";
}
}
).join("");
var distinct_item_names = [];
var distinct_items = [];
var taxable_amount = {};
$.each(this.frm.doc["items"] || [], function(i, item) {
var item_code = item.item_code || item.item_name;
if(distinct_item_names.indexOf(item_code)===-1) {
distinct_item_names.push(item_code);
distinct_items.push(item);
taxable_amount[item_code] = item.net_amount;
} else {
taxable_amount[item_code] = taxable_amount[item_code] + item.net_amount;
}
});
var rows = $.map(distinct_items, function(item) {
var item_code = item.item_code || item.item_name;
var item_tax_record = item_tax[item_code];
if(!item_tax_record) { return null; }
return repl("<tr><td>%(item_name)s</td><td class='text-right'>%(taxable_amount)s</td>%(taxes)s</tr>", {
item_name: item.item_name,
taxable_amount: format_currency(taxable_amount[item_code],
company_currency, precision("net_amount", item)),
taxes: $.map(tax_accounts, function(head) {
return item_tax_record[head[0]] ?
"<td class='text-right'>(" + item_tax_record[head[0]][0] + ") " + item_tax_record[head[0]][1] + "</td>" :
"<td></td>";
}).join("")
});
}).join("");
if(!rows) return "";
return '<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>';
}
})

View File

@@ -63,37 +63,32 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
});
frappe.ui.form.on(this.frm.doctype, "additional_discount_percentage", function(frm) {
if (frm.via_discount_amount) {
return;
}
if(!frm.doc.apply_discount_on) {
frappe.msgprint(__("Please set 'Apply Additional Discount On'"));
return
return;
}
frm.via_discount_percentage = true;
if(frm.doc.additional_discount_percentage && frm.doc.discount_amount) {
// Reset discount amount and net / grand total
frm.set_value("discount_amount", 0);
frm.doc.discount_amount = 0;
frm.cscript.calculate_taxes_and_totals();
}
var total = flt(frm.doc[frappe.model.scrub(frm.doc.apply_discount_on)]);
var discount_amount = flt(total*flt(frm.doc.additional_discount_percentage) / 100,
precision("discount_amount"));
frm.set_value("discount_amount", discount_amount);
delete frm.via_discount_percentage;
frm.set_value("discount_amount", discount_amount)
.then(() => delete frm.via_discount_percentage);
});
frappe.ui.form.on(this.frm.doctype, "discount_amount", function(frm) {
frm.cscript.set_dynamic_labels();
if (!frm.via_discount_percentage) {
frm.via_discount_amount = true;
frm.set_value("additional_discount_percentage", 0);
delete frm.via_discount_amount;
frm.doc.additional_discount_percentage = 0;
}
frm.cscript.calculate_taxes_and_totals();
@@ -374,6 +369,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
validate: function() {
this.calculate_taxes_and_totals(false);
this.set_item_wise_tax_breakup();
},
company: function() {
@@ -936,69 +932,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
});
},
get_item_wise_taxes_html: function() {
var item_tax = {};
var tax_accounts = [];
var company_currency = this.get_company_currency();
$.each(this.frm.doc["taxes"] || [], function(i, tax) {
var tax_amount_precision = precision("tax_amount", tax);
var tax_rate_precision = precision("rate", tax);
$.each(JSON.parse(tax.item_wise_tax_detail || '{}'),
function(item_code, tax_data) {
if(!item_tax[item_code]) item_tax[item_code] = {};
if($.isArray(tax_data)) {
var tax_rate = "";
if(tax_data[0] != null) {
tax_rate = (tax.charge_type === "Actual") ?
format_currency(flt(tax_data[0], tax_amount_precision), company_currency, tax_amount_precision) :
(flt(tax_data[0], tax_rate_precision) + "%");
}
var tax_amount = format_currency(flt(tax_data[1], tax_amount_precision), company_currency,
tax_amount_precision);
item_tax[item_code][tax.name] = [tax_rate, tax_amount];
} else {
item_tax[item_code][tax.name] = [flt(tax_data, tax_rate_precision) + "%", ""];
}
});
tax_accounts.push([tax.name, tax.account_head]);
});
var headings = $.map([__("Item Name")].concat($.map(tax_accounts, function(head) { return head[1]; })),
function(head) { return '<th style="min-width: 100px;">' + (head || "") + "</th>" }).join("\n");
var distinct_item_names = [];
var distinct_items = [];
$.each(this.frm.doc["items"] || [], function(i, item) {
if(distinct_item_names.indexOf(item.item_code || item.item_name)===-1) {
distinct_item_names.push(item.item_code || item.item_name);
distinct_items.push(item);
}
});
var rows = $.map(distinct_items, function(item) {
var item_tax_record = item_tax[item.item_code || item.item_name];
if(!item_tax_record) { return null; }
return repl("<tr><td>%(item_name)s</td>%(taxes)s</tr>", {
item_name: item.item_name,
taxes: $.map(tax_accounts, function(head) {
return item_tax_record[head[0]] ?
"<td>(" + item_tax_record[head[0]][0] + ") " + item_tax_record[head[0]][1] + "</td>" :
"<td></td>";
}).join("\n")
});
}).join("\n");
if(!rows) return "";
return '<p><a class="h6 text-muted" href="#" onclick="$(\'.tax-break-up\').toggleClass(\'hide\'); return false;">'
+ __("Show tax break-up") + '</a></p>\
<div class="tax-break-up hide" style="overflow-x: auto;"><table class="table table-bordered table-hover">\
<thead><tr>' + headings + '</tr></thead> \
<tbody>' + rows + '</tbody> \
</table></div>';
},
validate_company_and_party: function() {
var me = this;
var valid = true;
@@ -1046,18 +979,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
},
show_item_wise_taxes: function() {
if(this.frm.fields_dict.other_charges_calculation) {
var html = this.get_item_wise_taxes_html();
if (html) {
this.frm.toggle_display("other_charges_calculation", true);
$(this.frm.fields_dict.other_charges_calculation.wrapper).html(html);
} else {
this.frm.toggle_display("other_charges_calculation", false);
}
}
},
is_recurring: function() {
// set default values for recurring documents
if(this.frm.doc.is_recurring && this.frm.doc.__islocal) {

View File

@@ -1,4 +1,4 @@
frappe.provide("erpnext.wiz");
frappe.provide("erpnext.setup");
frappe.pages['setup-wizard'].on_page_load = function(wrapper) {
if(frappe.sys_defaults.company) {
@@ -7,459 +7,432 @@ frappe.pages['setup-wizard'].on_page_load = function(wrapper) {
}
};
function load_erpnext_slides() {
$.extend(erpnext.wiz, {
select_domain: {
domains: ["all"],
title: __('Select your Domain'),
fields: [
{fieldname:'domain', label: __('Domain'), fieldtype:'Select',
options: [
{"label": __("Distribution"), "value": "Distribution"},
{"label": __("Education"), "value": "Education"},
{"label": __("Manufacturing"), "value": "Manufacturing"},
{"label": __("Retail"), "value": "Retail"},
{"label": __("Services"), "value": "Services"}
], reqd:1},
],
help: __('Select the nature of your business.'),
onload: function(slide) {
slide.get_input("domain").on("change", function() {
frappe.wiz.domain = $(this).val();
frappe.wizard.refresh_slides();
});
var erpnext_slides = [
{
// Domain
name: 'domain',
domains: ["all"],
title: __('Select your Domain'),
fields: [
{
fieldname: 'domain', label: __('Domain'), fieldtype: 'Select',
options: [
{ "label": __("Distribution"), "value": "Distribution" },
{ "label": __("Education"), "value": "Education" },
{ "label": __("Manufacturing"), "value": "Manufacturing" },
{ "label": __("Retail"), "value": "Retail" },
{ "label": __("Services"), "value": "Services" }
], reqd: 1
},
css_class: "single-column"
],
help: __('Select the nature of your business.'),
onload: function (slide) {
slide.get_input("domain").on("change", function () {
frappe.setup.domain = $(this).val();
frappe.wizard.refresh_slides();
});
},
org: {
domains: ["all"],
title: __("The Organization"),
icon: "fa fa-building",
fields: [
{fieldname:'company_name',
label: frappe.wiz.domain==='Education' ?
__('Institute Name') : __('Company Name'),
fieldtype:'Data', reqd:1},
{fieldname:'company_abbr',
label: frappe.wiz.domain==='Education' ?
__('Institute Abbreviation') : __('Company Abbreviation'),
fieldtype:'Data'},
{fieldname:'company_tagline',
label: __('What does it do?'),
fieldtype:'Data',
placeholder: frappe.wiz.domain==='Education' ?
__('e.g. "Primary School" or "University"') :
__('e.g. "Build tools for builders"'),
reqd:1},
{fieldname:'bank_account', label: __('Bank Name'), fieldtype:'Data', reqd:1},
{fieldname:'chart_of_accounts', label: __('Chart of Accounts'),
options: "", fieldtype: 'Select'},
},
// TODO remove this
{fieldtype: "Section Break"},
{fieldname:'fy_start_date', label:__('Financial Year Start Date'), fieldtype:'Date',
description: __('Your financial year begins on'), reqd:1},
{fieldname:'fy_end_date', label:__('Financial Year End Date'), fieldtype:'Date',
description: __('Your financial year ends on'), reqd:1},
],
help: (frappe.wiz.domain==='Education' ?
__('The name of the institute for which you are setting up this system.'):
__('The name of your company for which you are setting up this system.')),
{
// Brand
name: 'brand',
domains: ["all"],
icon: "fa fa-bookmark",
title: __("The Brand"),
help: __('Upload your letter head and logo. (you can edit them later).'),
fields: [
{
fieldtype: "Attach Image", fieldname: "attach_logo",
label: __("Attach Logo"),
description: __("100px by 100px"),
is_private: 0
},
{
fieldname: 'company_name',
label: frappe.setup.domain === 'Education' ?
__('Institute Name') : __('Company Name'),
fieldtype: 'Data', reqd: 1
},
{
fieldname: 'company_abbr',
label: frappe.setup.domain === 'Education' ?
__('Institute Abbreviation') : __('Company Abbreviation'),
fieldtype: 'Data'
}
],
onload: function(slide) {
this.bind_events(slide);
},
bind_events: function (slide) {
slide.get_input("company_name").on("change", function () {
var parts = slide.get_input("company_name").val().split(" ");
var abbr = $.map(parts, function (p) { return p ? p.substr(0, 1) : null }).join("");
slide.get_field("company_abbr").set_value(abbr.slice(0, 5).toUpperCase());
}).val(frappe.boot.sysdefaults.company_name || "").trigger("change");
onload: function(slide) {
erpnext.wiz.org.load_chart_of_accounts(slide);
erpnext.wiz.org.bind_events(slide);
erpnext.wiz.org.set_fy_dates(slide);
slide.get_input("company_abbr").on("change", function () {
if (slide.get_input("company_abbr").val().length > 5) {
frappe.msgprint("Company Abbreviation cannot have more than 5 characters");
slide.get_field("company_abbr").set_value("");
}
});
}
},
{
// Organisation
name: 'organisation',
domains: ["all"],
title: __("Your Organization"),
icon: "fa fa-building",
help: (frappe.setup.domain === 'Education' ?
__('The name of the institute for which you are setting up this system.') :
__('The name of your company for which you are setting up this system.')),
fields: [
{
fieldname: 'company_tagline',
label: __('What does it do?'),
fieldtype: 'Data',
placeholder: frappe.setup.domain === 'Education' ?
__('e.g. "Primary School" or "University"') :
__('e.g. "Build tools for builders"'),
reqd: 1
},
{ fieldname: 'bank_account', label: __('Bank Name'), fieldtype: 'Data', reqd: 1 },
{
fieldname: 'chart_of_accounts', label: __('Chart of Accounts'),
options: "", fieldtype: 'Select'
},
validate: function() {
// validate fiscal year start and end dates
if (this.values.fy_start_date=='Invalid date' || this.values.fy_end_date=='Invalid date') {
frappe.msgprint(__("Please enter valid Financial Year Start and End Dates"));
return false;
{ fieldtype: "Section Break", label: "Financial Year" },
{ fieldname: 'fy_start_date', label: __('Start Date'), fieldtype: 'Date', reqd: 1 },
{ fieldtype: "Column Break" },
{ fieldname: 'fy_end_date', label: __('End Date'), fieldtype: 'Date', reqd: 1 },
],
onload: function (slide) {
this.load_chart_of_accounts(slide);
this.bind_events(slide);
this.set_fy_dates(slide);
},
validate: function () {
// validate fiscal year start and end dates
if (this.values.fy_start_date == 'Invalid date' || this.values.fy_end_date == 'Invalid date') {
frappe.msgprint(__("Please enter valid Financial Year Start and End Dates"));
return false;
}
if ((this.values.company_name || "").toLowerCase() == "company") {
frappe.msgprint(__("Company Name cannot be Company"));
return false;
}
return true;
},
set_fy_dates: function (slide) {
var country = frappe.wizard.values.country;
if (country) {
var fy = erpnext.setup.fiscal_years[country];
var current_year = moment(new Date()).year();
var next_year = current_year + 1;
if (!fy) {
fy = ["01-01", "12-31"];
next_year = current_year;
}
if ((this.values.company_name || "").toLowerCase() == "company") {
frappe.msgprint(__("Company Name cannot be Company"));
return false;
var year_start_date = current_year + "-" + fy[0];
if (year_start_date > frappe.datetime.get_today()) {
next_year = current_year;
current_year -= 1;
}
slide.get_field("fy_start_date").set_value(current_year + '-' + fy[0]);
slide.get_field("fy_end_date").set_value(next_year + '-' + fy[1]);
}
return true;
},
},
css_class: "single-column",
set_fy_dates: function(slide) {
var country = frappe.wizard.values.country;
load_chart_of_accounts: function (slide) {
var country = frappe.wizard.values.country;
if(country) {
var fy = erpnext.wiz.fiscal_years[country];
var current_year = moment(new Date()).year();
var next_year = current_year + 1;
if(!fy) {
fy = ["01-01", "12-31"];
next_year = current_year;
}
var year_start_date = current_year + "-" + fy[0];
if(year_start_date > frappe.datetime.get_today()) {
next_year = current_year
current_year -= 1;
}
slide.get_field("fy_start_date").set_input(current_year + "-" + fy[0]);
slide.get_field("fy_end_date").set_input(next_year + "-" + fy[1]);
}
if (country) {
frappe.call({
method: "erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts.get_charts_for_country",
args: { "country": country },
callback: function (r) {
if (r.message) {
slide.get_input("chart_of_accounts").empty()
.add_options(r.message);
},
load_chart_of_accounts: function(slide) {
var country = frappe.wizard.values.country;
if(country) {
frappe.call({
method: "erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts.get_charts_for_country",
args: {"country": country},
callback: function(r) {
if(r.message) {
slide.get_input("chart_of_accounts").empty()
.add_options(r.message);
if (r.message.length===1) {
var field = slide.get_field("chart_of_accounts");
field.set_value(r.message[0]);
field.df.hidden = 1;
field.refresh();
}
if (r.message.length === 1) {
var field = slide.get_field("chart_of_accounts");
field.set_value(r.message[0]);
field.df.hidden = 1;
field.refresh();
}
}
})
}
},
bind_events: function(slide) {
slide.get_input("company_name").on("change", function() {
var parts = slide.get_input("company_name").val().split(" ");
var abbr = $.map(parts, function(p) { return p ? p.substr(0,1) : null }).join("");
slide.get_field("company_abbr").set_input(abbr.slice(0, 5).toUpperCase());
}).val(frappe.boot.sysdefaults.company_name || "").trigger("change");
slide.get_input("company_abbr").on("change", function() {
if(slide.get_input("company_abbr").val().length > 5) {
frappe.msgprint("Company Abbreviation cannot have more than 5 characters");
slide.get_field("company_abbr").set_input("");
}
});
// TODO remove this
slide.get_input("fy_start_date").on("change", function() {
var year_end_date =
frappe.datetime.add_days(frappe.datetime.add_months(
frappe.datetime.user_to_obj(slide.get_input("fy_start_date").val()), 12), -1);
slide.get_input("fy_end_date").val(frappe.datetime.obj_to_user(year_end_date));
});
})
}
},
branding: {
domains: ["all"],
icon: "fa fa-bookmark",
title: __("The Brand"),
help: __('Upload your letter head and logo. (you can edit them later).'),
fields: [
{fieldtype:"Attach Image", fieldname:"attach_letterhead",
label: __("Attach Letterhead"),
description: __("Keep it web friendly 900px (w) by 100px (h)"),
is_private: 0
},
{fieldtype: "Column Break"},
{fieldtype:"Attach Image", fieldname:"attach_logo",
label:__("Attach Logo"),
description: __("100px by 100px"),
is_private: 0
},
],
bind_events: function (slide) {
slide.get_input("fy_start_date").on("change", function () {
var start_date = slide.form.fields_dict.fy_start_date.get_value();
var year_end_date =
frappe.datetime.add_days(frappe.datetime.add_months(start_date, 12), -1);
slide.form.fields_dict.fy_end_date.set_value(year_end_date);
});
}
},
css_class: "two-column"
},
{
// Users
name: 'users',
domains: ["all"],
title: __("Add Users"),
help: __("Add users to your organization, other than yourself"),
add_more: 1,
max_count: 3,
fields: [
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"user_fullname",
label:__("Full Name"), static: 1},
{fieldtype:"Data", fieldname:"user_email", label:__("Email ID"),
placeholder:__("user@example.com"), options: "Email", static: 1},
{fieldtype:"Column Break"},
{fieldtype: "Check", fieldname: "user_sales",
label:__("Sales"), "default": 1, static: 1,
hidden: frappe.setup.domain==='Education' ? 1 : 0},
{fieldtype: "Check", fieldname: "user_purchaser",
label:__("Purchaser"), "default": 1, static: 1,
hidden: frappe.setup.domain==='Education' ? 1 : 0},
{fieldtype: "Check", fieldname: "user_accountant",
label:__("Accountant"), "default": 1, static: 1,
hidden: frappe.setup.domain==='Education' ? 1 : 0},
]
},
users: {
domains: ["all"],
icon: "fa fa-money",
title: __("Add Users"),
help: __("Add users to your organization, other than yourself"),
fields: [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<5; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"user_fullname_"+ i,
label:__("Full Name")},
{fieldtype:"Data", fieldname:"user_email_" + i,
label:__("Email Address"), placeholder:__("user@example.com"),
options: "Email"},
{fieldtype:"Column Break"},
{fieldtype: "Check", fieldname: "user_sales_" + i,
label:__("Sales"), "default": 1,
hidden: frappe.wiz.domain==='Education' ? 1 : 0},
{fieldtype: "Check", fieldname: "user_purchaser_" + i,
label:__("Purchaser"), "default": 1,
hidden: frappe.wiz.domain==='Education' ? 1 : 0},
{fieldtype: "Check", fieldname: "user_accountant_" + i,
label:__("Accountant"), "default": 1,
hidden: frappe.wiz.domain==='Education' ? 1 : 0},
]);
}
},
css_class: "two-column"
},
{
// Taxes
name: 'taxes',
domains: ['manufacturing', 'services', 'retail', 'distribution'],
icon: "fa fa-money",
title: __("Add Taxes"),
help: __("List your tax heads (e.g. VAT, Customs etc; they should have unique names) and their standard rates. This will create a standard template, which you can edit and add more later."),
add_more: 1,
max_count: 3,
mandatory_entry: 1,
fields: [
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"tax", label:__("Tax"),
placeholder:__("e.g. VAT")},
{fieldtype:"Column Break"},
{fieldtype:"Float", fieldname:"tax_rate", label:__("Rate (%)"), placeholder:__("e.g. 5")}
]
},
taxes: {
domains: ['manufacturing', 'services', 'retail', 'distribution'],
icon: "fa fa-money",
title: __("Add Taxes"),
help: __("List your tax heads (e.g. VAT, Customs etc; they should have unique names) and their standard rates. This will create a standard template, which you can edit and add more later."),
"fields": [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<4; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"tax_"+ i, label:__("Tax") + " " + i,
placeholder:__("e.g. VAT") + " " + i},
{fieldtype:"Column Break"},
{fieldtype:"Float", fieldname:"tax_rate_" + i, label:__("Rate (%)"), placeholder:__("e.g. 5")},
]);
}
},
css_class: "two-column"
},
{
// Customers
name: 'customers',
domains: ['manufacturing', 'services', 'retail', 'distribution'],
icon: "fa fa-group",
title: __("Add Customers"),
help: __("List a few of your customers. They could be organizations or individuals."),
add_more: 1,
max_count: 5,
mandatory_entry: 1,
fields: [
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"customer", label:__("Customer"),
placeholder:__("Customer Name")},
{fieldtype:"Column Break"},
{fieldtype:"Data", fieldname:"customer_contact",
label:__("Contact Name"), placeholder:__("Contact Name")}
],
},
customers: {
domains: ['manufacturing', 'services', 'retail', 'distribution'],
icon: "fa fa-group",
title: __("Your Customers"),
help: __("List a few of your customers. They could be organizations or individuals."),
fields: [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<6; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"customer_" + i, label:__("Customer") + " " + i,
placeholder:__("Customer Name")},
{fieldtype:"Column Break"},
{fieldtype:"Data", fieldname:"customer_contact_" + i,
label:__("Contact Name") + " " + i, placeholder:__("Contact Name")}
])
}
slide.fields[1].reqd = 1;
},
css_class: "two-column"
},
{
// Suppliers
name: 'suppliers',
domains: ['manufacturing', 'services', 'retail', 'distribution'],
icon: "fa fa-group",
title: __("Your Suppliers"),
help: __("List a few of your suppliers. They could be organizations or individuals."),
add_more: 1,
max_count: 5,
mandatory_entry: 1,
fields: [
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"supplier", label:__("Supplier"),
placeholder:__("Supplier Name")},
{fieldtype:"Column Break"},
{fieldtype:"Data", fieldname:"supplier_contact",
label:__("Contact Name"), placeholder:__("Contact Name")},
]
},
suppliers: {
domains: ['manufacturing', 'services', 'retail', 'distribution'],
icon: "fa fa-group",
title: __("Your Suppliers"),
help: __("List a few of your suppliers. They could be organizations or individuals."),
fields: [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<6; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"supplier_" + i, label:__("Supplier")+" " + i,
placeholder:__("Supplier Name")},
{fieldtype:"Column Break"},
{fieldtype:"Data", fieldname:"supplier_contact_" + i,
label:__("Contact Name") + " " + i, placeholder:__("Contact Name")},
])
}
slide.fields[1].reqd = 1;
},
css_class: "two-column"
},
{
// Products
name: 'products',
domains: ['manufacturing', 'services', 'retail', 'distribution'],
icon: "fa fa-barcode",
title: __("Your Products or Services"),
help: __("List your products or services that you buy or sell. Make sure to check the Item Group, Unit of Measure and other properties when you start."),
add_more: 1,
max_count: 5,
mandatory_entry: 1,
fields: [
{fieldtype:"Section Break", show_section_border: true},
{fieldtype:"Data", fieldname:"item", label:__("Item"),
placeholder:__("A Product or Service")},
{fieldtype:"Select", label:__("Group"), fieldname:"item_group",
options:[__("Products"), __("Services"),
__("Raw Material"), __("Consumable"), __("Sub Assemblies")],
"default": __("Products"), static: 1},
{fieldtype:"Select", fieldname:"item_uom", label:__("UOM"),
options:[__("Unit"), __("Nos"), __("Box"), __("Pair"), __("Kg"), __("Set"),
__("Hour"), __("Minute"), __("Litre"), __("Meter"), __("Gram")],
"default": __("Unit"), static: 1},
{fieldtype: "Check", fieldname: "is_sales_item", label:__("We sell this Item"), default: 1, static: 1},
{fieldtype: "Check", fieldname: "is_purchase_item", label:__("We buy this Item"), static: 1},
{fieldtype:"Column Break"},
{fieldtype:"Currency", fieldname:"item_price", label:__("Rate"), static: 1},
{fieldtype:"Attach Image", fieldname:"item_img", label:__("Attach Image"), is_private: 0, static: 1},
],
get_item_count: function() {
return this.item_count;
}
},
items: {
domains: ['manufacturing', 'services', 'retail', 'distribution'],
icon: "fa fa-barcode",
title: __("Your Products or Services"),
help: __("List your products or services that you buy or sell. Make sure to check the Item Group, Unit of Measure and other properties when you start."),
fields: [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<6; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break", show_section_border: true},
{fieldtype:"Data", fieldname:"item_" + i, label:__("Item") + " " + i,
placeholder:__("A Product or Service")},
{fieldtype:"Select", label:__("Group"), fieldname:"item_group_" + i,
options:[__("Products"), __("Services"),
__("Raw Material"), __("Consumable"), __("Sub Assemblies")],
"default": __("Products")},
{fieldtype:"Select", fieldname:"item_uom_" + i, label:__("UOM"),
options:[__("Unit"), __("Nos"), __("Box"), __("Pair"), __("Kg"), __("Set"),
__("Hour"), __("Minute"), __("Litre"), __("Meter"), __("Gram")],
"default": __("Unit")},
{fieldtype: "Check", fieldname: "is_sales_item_" + i, label:__("We sell this Item"), default: 1},
{fieldtype: "Check", fieldname: "is_purchase_item_" + i, label:__("We buy this Item")},
{fieldtype:"Column Break"},
{fieldtype:"Currency", fieldname:"item_price_" + i, label:__("Rate")},
{fieldtype:"Attach Image", fieldname:"item_img_" + i, label:__("Attach Image"), is_private: 0},
])
}
slide.fields[1].reqd = 1;
{
// Program
name: 'program',
domains: ["education"],
title: __("Program"),
help: __("Example: Masters in Computer Science"),
add_more: 1,
max_count: 5,
mandatory_entry: 1,
fields: [
{fieldtype:"Section Break", show_section_border: true},
{fieldtype:"Data", fieldname:"program", label:__("Program"), placeholder: __("Program Name")},
],
},
// dummy data
slide.fields.push({fieldtype: "Section Break"});
slide.fields.push({fieldtype: "Check", fieldname: "add_sample_data",
label: __("Add a few sample records"), "default": 1});
slide.fields.push({fieldtype: "Check", fieldname: "setup_website",
label: __("Setup a simple website for my organization"), "default": 1});
},
css_class: "two-column"
},
{
// Course
name: 'course',
domains: ["education"],
title: __("Course"),
help: __("Example: Basic Mathematics"),
add_more: 1,
max_count: 5,
mandatory_entry: 1,
fields: [
{fieldtype:"Section Break", show_section_border: true},
{fieldtype:"Data", fieldname:"course", label:__("Course"), placeholder: __("Course Name")},
]
},
program: {
domains: ["education"],
title: __("Program"),
help: __("Example: Masters in Computer Science"),
fields: [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<6; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break", show_section_border: true},
{fieldtype:"Data", fieldname:"program_" + i, label:__("Program") + " " + i, placeholder: __("Program Name")},
])
}
slide.fields[1].reqd = 1;
},
css_class: "single-column"
},
{
// Instructor
name: 'instructor',
domains: ["education"],
title: __("Instructor"),
help: __("People who teach at your organisation"),
add_more: 1,
max_count: 5,
mandatory_entry: 1,
fields: [
{fieldtype:"Section Break", show_section_border: true},
{fieldtype:"Data", fieldname:"instructor", label:__("Instructor"), placeholder: __("Instructor Name")},
]
},
course: {
domains: ["education"],
title: __("Course"),
help: __("Example: Basic Mathematics"),
fields: [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<6; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break", show_section_border: true},
{fieldtype:"Data", fieldname:"course_" + i, label:__("Course") + " " + i, placeholder: __("Course Name")},
])
}
slide.fields[1].reqd = 1;
},
css_class: "single-column"
},
{
// Room
name: 'room',
domains: ["education"],
title: __("Room"),
help: __("Classrooms/ Laboratories etc where lectures can be scheduled."),
add_more: 1,
max_count: 3,
mandatory_entry: 1,
fields: [
{fieldtype:"Section Break", show_section_border: true},
{fieldtype:"Data", fieldname:"room", label:__("Room")},
{fieldtype:"Column Break"},
{fieldtype:"Int", fieldname:"room_capacity", label:__("Room") + " Capacity", static: 1},
]
},
instructor: {
domains: ["education"],
title: __("Instructor"),
help: __("People who teach at your organisation"),
fields: [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<6; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break", show_section_border: true},
{fieldtype:"Data", fieldname:"instructor_" + i, label:__("Instructor") + " " + i, placeholder: __("Instructor Name")},
])
}
slide.fields[1].reqd = 1;
},
css_class: "single-column"
},
room: {
domains: ["education"],
title: __("Room"),
help: __("Classrooms/ Laboratories etc where lectures can be scheduled."),
fields: [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<4; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break", show_section_border: true},
{fieldtype:"Data", fieldname:"room_" + i, label:__("Room") + " " + i},
{fieldtype:"Column Break"},
{fieldtype:"Int", fieldname:"room_capacity_" + i, label:__("Room") + " " + i + " Capacity"},
])
}
slide.fields[1].reqd = 1;
},
css_class: "two-column"
},
});
// Source: https://en.wikipedia.org/wiki/Fiscal_year
// default 1st Jan - 31st Dec
erpnext.wiz.fiscal_years = {
"Afghanistan": ["12-20", "12-21"],
"Australia": ["07-01", "06-30"],
"Bangladesh": ["07-01", "06-30"],
"Canada": ["04-01", "03-31"],
"Costa Rica": ["10-01", "09-30"],
"Egypt": ["07-01", "06-30"],
"Hong Kong": ["04-01", "03-31"],
"India": ["04-01", "03-31"],
"Iran": ["06-23", "06-22"],
"Italy": ["07-01", "06-30"],
"Myanmar": ["04-01", "03-31"],
"New Zealand": ["04-01", "03-31"],
"Pakistan": ["07-01", "06-30"],
"Singapore": ["04-01", "03-31"],
"South Africa": ["03-01", "02-28"],
"Thailand": ["10-01", "09-30"],
"United Kingdom": ["04-01", "03-31"],
};
}
frappe.wiz.on("before_load", function() {
load_erpnext_slides();
frappe.wiz.add_slide(erpnext.wiz.select_domain);
frappe.wiz.add_slide(erpnext.wiz.org);
frappe.wiz.add_slide(erpnext.wiz.branding);
if (!(frappe.boot.limits && frappe.boot.limits.users===1)) {
frappe.wiz.add_slide(erpnext.wiz.users);
{
// last slide: Sample Data
name: 'bootstrap',
domains: ["all"],
title: __("Sample Data"),
fields: [{fieldtype: "Section Break"},
{fieldtype: "Check", fieldname: "add_sample_data",
label: __("Add a few sample records"), "default": 1},
{fieldtype: "Check", fieldname: "setup_website",
label: __("Setup a simple website for my organization"), "default": 1}
]
}
];
frappe.wiz.add_slide(erpnext.wiz.taxes);
frappe.wiz.add_slide(erpnext.wiz.customers);
frappe.wiz.add_slide(erpnext.wiz.suppliers);
frappe.wiz.add_slide(erpnext.wiz.items);
frappe.wiz.add_slide(erpnext.wiz.program);
frappe.wiz.add_slide(erpnext.wiz.course);
frappe.wiz.add_slide(erpnext.wiz.instructor);
frappe.wiz.add_slide(erpnext.wiz.room);
// Source: https://en.wikipedia.org/wiki/Fiscal_year
// default 1st Jan - 31st Dec
if(frappe.wizard && frappe.wizard.domain && frappe.wizard.domain !== 'Education') {
frappe.wiz.welcome_page = "#welcome-to-erpnext";
erpnext.setup.fiscal_years = {
"Afghanistan": ["12-20", "12-21"],
"Australia": ["07-01", "06-30"],
"Bangladesh": ["07-01", "06-30"],
"Canada": ["04-01", "03-31"],
"Costa Rica": ["10-01", "09-30"],
"Egypt": ["07-01", "06-30"],
"Hong Kong": ["04-01", "03-31"],
"India": ["04-01", "03-31"],
"Iran": ["06-23", "06-22"],
"Italy": ["07-01", "06-30"],
"Myanmar": ["04-01", "03-31"],
"New Zealand": ["04-01", "03-31"],
"Pakistan": ["07-01", "06-30"],
"Singapore": ["04-01", "03-31"],
"South Africa": ["03-01", "02-28"],
"Thailand": ["10-01", "09-30"],
"United Kingdom": ["04-01", "03-31"],
};
frappe.setup.on("before_load", function () {
erpnext_slides.map(frappe.setup.add_slide);
// change header brand
let $brand = $('header .setup-wizard-brand');
if($brand.find('.erpnext-icon').length === 0) {
$brand.find('.frappe-icon').hide();
$brand.append(`<span>
<img src="/assets/erpnext/images/erp-icon.svg" class="brand-icon erpnext-icon"
style="width:36px;"><span class="brand-name">ERPNext</span></span>`);
}
});
var test_values_edu = {
"language":"english",
"domain":"Education",
"country":"India",
"timezone":"Asia/Kolkata",
"currency":"INR",
"first_name":"Tester",
"email":"test@example.com",
"password":"test",
"company_name":"Hogwarts",
"company_abbr":"HS",
"company_tagline":"School for magicians",
"bank_account":"Gringotts Wizarding Bank",
"fy_start_date":"2016-04-01",
"fy_end_date":"2017-03-31"
"language": "english",
"domain": "Education",
"country": "India",
"timezone": "Asia/Kolkata",
"currency": "INR",
"first_name": "Tester",
"email": "test@example.com",
"password": "test",
"company_name": "Hogwarts",
"company_abbr": "HS",
"company_tagline": "School for magicians",
"bank_account": "Gringotts Wizarding Bank",
"fy_start_date": "2016-04-01",
"fy_end_date": "2017-03-31"
}

View File

@@ -72,5 +72,5 @@ state_numbers = {
"Tripura": "16",
"Uttar Pradesh": "35",
"Uttarakhand": "36",
"West Bengal": "37"
"West Bengal": "19"
}

View File

@@ -1,8 +1,9 @@
{{ address_line1 }}<br>{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}{{ city }}<br>
{% if state %}{{ state }}<br>{% endif -%}
{% if pincode %}{{ pincode }}<br>{% endif -%}
{% if gst_state %}{{ gst_state }}{% endif -%},
{% if gst_state_number %}State Code: {{ gst_state_number }}<br>{% endif -%}
{% if pincode %}PIN: {{ pincode }}<br>{% endif -%}
{{ country }}<br>
{% if gstin %}GSTIN: {{ gstin }}<br>{% endif -%}
{% if phone %}Phone: {{ phone }}<br>{% endif -%}
{% if fax %}Fax: {{ fax }}<br>{% endif -%}
{% if email_id %}Email: {{ email_id }}<br>{% endif -%}
{% if email_id %}Email: {{ email_id }}<br>{% endif -%}
{% if gstin %}GSTIN: {{ gstin }}<br>{% endif -%}

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