You are on page 1of 43

ERPNext 2​nd​ Edition Ebook

Contents

Chapter 1: Environment setup for ERPNext 5


Step 1 Update and Upgrade APT 5
Step 2 Install Python 6
Step 3 Install Dependencies 6
Step 4 Install Python pip Tool 6
Step 5 Install curl and yarn 7
Step 6 Install MariaDB 8
Step 7 Install Nginx, Node.js and Redis 9
Step 8 Install wkhtmltopdf and fonts 10
Step 9 Install and Create Virtual Environment 11
Step 10 Install Bench 12
Step 11 Create a new bench 12
Step 12 Get ERPNext Apps 12
Step 13 Create new site 12
Step 14 Install Apps (Install ERPNext on Ubuntu 18.04) 13
Step 15 Start bench 13

Chapter 2: Create a custom application 14

Chapter 3: ERPNext Live Example 15


Example 1: Doctype Events (Server side) 15
Example 2: Environment setup for DNS based Multitenant. 18
Example 3: Create parent child category using custom app. 20
Example 4: Customize customer status using custom app. 23
Example 5: Frappe.get_doc using custom app. 25
Example 6: Frappe call method using custom app 26
Example 7: Creating app icon in desk 30
Example 8: Script Report by using custom app 32
1. Goto developer > click on "Report" > Create a new Report 32
2 In the .py file you can add the script for generating the report. 33
3. Add link for your report on the module page 34
Example 9: Customization of sale invoice report template using custom app 36
1) Created custom app: tax_invoice 36
2) Customization standard.html file 36

2 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

3) Override standard.html file hook.py > custom app 42


4) Screen shot 43

References: 44

ERPNext 2nd edition Ebook 2

ERPNext 2nd edition Ebook 1

3 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Chapter 1: Environment setup for ERPNext

Install ERPNext on Ubuntu 18.04

Steps for Install ERPNext on Ubuntu 18.04

Step 1 Update and Upgrade APT


Update APT list of available packages and their versions. And use ​upgrade ​command to
actually installs newer versions of the packages.

sudo apt-get update && sudo apt-get upgrade

4 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Step 2 Install Python


First of all, you have to install Python 2.7 for ERPNext. Moreover, you can

also verify Python version.

sudo apt -y install python-minimal

$ python -V

Python 2.7.15rc1

Step 3 Install Dependencies


You have to install the following dependencies.

sudo apt -y install git build-essential python-setuptools python-dev libffi-dev libssl-dev

Step 4 Install Python pip Tool


You need to execute the following command to install ​pip ​package manager.

wget https://bootstrap.pypa.io/get-pip.py

sudo python get-pip.py

5 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Execute the following command to verify that you have the latest version of ​pip ​and
setuptools​.

sudo pip install --upgrade pip setuptools

Install the ​ansible ​module using ​pip​. Ansible pip module automates manage Python
libraries and configuration.

sudo pip install ansible

Step 5 Install curl and yarn


First, you have to install ​curl​.

sudo apt -y install curl

Later you have to configure the ​yarn ​package repository.

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -

echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee


/etc/apt/sources.list.d/yarn.list

Next, execute the following command to install ​yarn​.

sudo apt -y update && sudo apt -y install yarn

6 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Step 6 Install MariaDB


You have to run the following command to install MariaDB.

sudo apt -y install mariadb-server libmysqlclient-dev

When creating ERPNext database, you need to enable barracuda storage engine. So
you first need to configure MariaDB configuration ​my.cnf ​file.

sudo nano /etc/mysql/my.cnf

Add the following lines at the end of ​my.cnf ​file.

[mysqld]
innodb-file-format=barracuda
innodb-file-per-table=1
innodb-large-prefix=1
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

[mysql]
default-character-set = utf8mb4

Next, you have to restart MariaDB and enable it to auto start MariaDB at boot time.

sudo systemctl restart mariadb

sudo systemctl enable mariadb

7 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Use the ​mysql_secure_installation ​tool to configure additional security options.

sudo mysql_secure_installation

This tool will ask if you want to set a new password for the MySQL root user and few
security related questions.

Set root password? [Y/n] y


New password:
Re-enter the new password:
Password updated successfully!

Security questions, answer at the following prompts:

Remove anonymous users?


Disallow root login remotely?
Remove test database and access to it?
Reload privilege tables now?

Step 7 Install Nginx, Node.js and Redis

You have to add the Node source Node.js 8.x repository.

sudo curl --silent --location https://deb.nodesource.com/setup_8.x | sudo bash -

To install Nginx, Node.js and Redis on Ubuntu, run the commands below.

sudo apt -y install nginx nodejs redis-server

8 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

After installing, start and enable Nginx service to always start up with the boots.

sudo systemctl start nginx

sudo systemctl enable nginx

Start and enable Redis service to always start up with the boots.

sudo systemctl start redis-server

sudo systemctl enable redis-server

Step 8 Install wkhtmltopdf and fonts


Execute the following command to install ​wkhtmltopdf​, fonts and other required
dependencies.

sudo apt -y install libxrender1 libxext6 xfonts-75dpi xfonts-base

Download the compressed file of wkhtmltopdf and extract to ​/opt ​path.

wget https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/
sudo tar -xf wkhtmltox-0.12.4_linux-generic-amd64.tar.xz -C /opt

Next, you have to create a soft link to access and execute wkhtmltopdf and
wkhtmltoimage globally as a command.

sudo ln -s /opt/wkhtmltox/bin/wkhtmltopdf /usr/bin/wkhtmltopdf


sudo ln -s /opt/wkhtmltox/bin/wkhtmltoimage /usr/bin/wkhtmltoimage

9 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Even more, you can check the version of wkhtmltopdf.

$ wkhtmltopdf -V
wkhtmltopdf 0.12.4 (with patched qt)

Step 9 Install and Create Virtual Environment


A virtual environment is a tool that helps to keep separate dependencies by different
projects to create isolated python virtual environments for them. A virtual environment is
the essential tools that most of the Python developers use.First of all, in your home
directory create new folder. Go to that folder.

mkdir erpnext
cd erpnext/

Run below command to install virtual environment.

sudo apt install virtualenv

Furthermore, you need to create virtual environment on “erpnext” directory using the
following command.

virtualenv .

Even more, you have to activate virtual environment using below command.

source ./bin/activate

10 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Step 10 Install Bench


Bench is a command line utility to install, manage multiple sites and manage the
ERPNext application on a Unix-based system.

Step 11 Create a new bench


The ​init​ ​command will create a bench directory with frappe framework installed.

git clone https://github.com/frappe/bench bench-repo


sudo pip install -e bench-repo

Now you have to go ​frappe-bench ​directory,

cd frappe-bench/

Step 12 Get ERPNext Apps


Bench ​get-app​ ​command gets remote frappe apps from a remote git repository and
installs them.

bench get-app --branch version-12 erpnext https://github.com/frappe/erpnext

Step 13 Create new site


Frappe sites run frappe apps. So you have to create at least one site. Using the
following command to create a new site.

bench new-site demosite

11 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Step 14 Install Apps (Install ERPNext on Ubuntu 18.04)


Use the bench ​install-app ​command to install an app on your site.

bench --site demosite install-app erpnext

Step 15 Start bench


Use the bench start command to start using the bench.

bench start

Finally, go to your web browser open your server IP address with 8000 port number
http://0.0.0.0:8000​ ​and you will see the ERPNext login screen. Here you have to log in with
username “administrator” and password as per you set when you created the new site.

12 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Chapter 2: Create a custom application

Build apps after creating site:

1) Create a new apps:


frappe-bench$ bench new-app <<apps name>>

$ bench new-app school_management


App Title (defaut: Lib Mgt): School Management
App Description: App ​for​ managing student, faculty, student ​and​ transactions ​for​ teacher and
examination
App Publisher: solufy
App Email: contact@solufy.in
App Icon (default ​'octicon octicon-file-directory'​): octicon octicon-book
App Color (default ​'grey'​): ​#589494
App License (default ​'MIT'​): GNU General Public License

2) Install site:
frappe-bench$ bench --site <<site name>> install-app <<apps name>>

3) Start bench:
frappe-bench$ bench start

4) performed env command


./env/bin/pip install -q -e ./apps/test_demo_app

13 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Chapter 3: ERPNext Live Example

Example 1: Doctype Events (Server side)

Doctype Events Server side


To execute code when a DocType is inserted, validated (before saving), updated, submitted,
cancelled, deleted, you must write in the DocType's controller module.

1. Controller Module
A controller template is created when the DocType is created. which looks like

from __future__ import unicode_literals

import frappe
from frappe.model.document import Document

class EbookExample(Document):
pass

2. Document Properties
All the fields and child tables are available to the class as attributes. For example the name
property is self.name

3. Adding Methods
In this module, you can add standard methods to the class that are called when a document of
that type is created. Standard Handlers are:
validate
on_submit
on_cancel
before_insert
on_update
on_trash
autoname
before_save
after_insert
before_submit
before_cancel
before_update_after_submit
on_update_after_submit

14 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

on_change
after_delete

Example of doc event (Server side):


1: In the hook.py file you will define doc_events

# Document Events
# ---------------
doc_events = {
"Item" :{
"validate" :
"sample_demo.sample_demo.doctype.ebook_example.ebook_example.call_validate",
},
"Sales Order" :{
"validate" :
"sample_demo.sample_demo.doctype.ebook_example.ebook_example.validate",
"on_submit" :
"sample_demo.sample_demo.doctype.ebook_example.ebook_example.on_submit",
"on_cancel" :
"sample_demo.sample_demo.doctype.ebook_example.ebook_example.on_cancel",
"before_insert" :
"sample_demo.sample_demo.doctype.ebook_example.ebook_example.before_insert",
"on_update" :
"sample_demo.sample_demo.doctype.ebook_example.ebook_example.on_update",
"on_trash" :
"sample_demo.sample_demo.doctype.ebook_example.ebook_example.on_trash"
},
"Ebook Example" :{
"autoname" :
"sample_demo.sample_demo.doctype.ebook_example.ebook_example.autoname",
},

# ebook_example.py
# -----------------------

from __future__ import unicode_literals


import frappe
from frappe.model.document import Document

15 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

from frappe import msgprint, _


from frappe.model.naming import make_autoname
from frappe.model.naming import get_default_naming_series

class EbookExample(Document):
pass

def call_validate(self,cdt):
frappe.msgprint(_("Validating"))
print ("\n CALL DOC ID:::::::::::::::",self.name)

doc = frappe.get_doc('Item',self.name)
print ("\n CALL DOC OBJECT::::::::::::::::::",doc)
print ("\n item_name::::::::::::::::::",doc.item_name)

def validate(self,cdt):
frappe.msgprint(_("Validating"))

def on_submit(self,cdt):
frappe.msgprint(_("Submiting"))
print ("\n CALL DOC ID:::::::::::::::",self.name)

doc = frappe.get_doc('Sales Order',self.name)


print ("\n CALL DOC OBJECT::::::::::::::::::",doc)
print ("\n customer_name::::::::::::::::::",doc.customer_name)

def on_cancel(self,cdt):
frappe.msgprint(_("Canceling"))

def before_insert(self,cdt):
frappe.msgprint(_("Inserting"))

def on_update(self,cdt):
frappe.msgprint(_("Updating"))

def on_trash(self,cdt):
frappe.msgprint(_("Trashing"))

def autoname(self,cdt):
frappe.msgprint(_("autoname"))

16 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Example 2​: Environment setup for DNS based Multitenant.

Concept: ​Multi tenant feature is maintain separate database for single server, so that record
created in one site(site1.local) should not be visible in other site(site2.local).

17 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Create multi tenant:

1. bench config dns_multitenant on


2. bench new-site site1.local
3. bench setup nginx
4. sudo service nginx reload
5. bench --site site1.local install-app erpnext
6. Repeat steps 1 - 5 for every other site you wish to create

just go in /etc/hosts
and define the domain
for ex:
127.0.0.1 site1.local
127.0.0.1 site2.local
127.0.0.1 site3.local

Ctrl + X and Y and Enter

make sure site name and domain name must be same.


Make sure always file " currentsite.txt" should be blank.

http://site1.local:8000/desk
http://site2.local:8000/desk
http://site3.local:8000/desk

Backup:
Take a backup of you ERPNext site by executing following command: bench --site site1.local
backup
backup created by default : /workspace/erpnext/frappe-bench/sites/site1.local/private

Restore database:
bench --site site2.local --force restore bench --site site2.local --force restore
/home/serpentcs/workspace/erpnext/frappe-bench/sites/site1.local/private/backups/20200108_1
60525-site1_local-database.sql.gz

Delete database:
bench drop-site site2.local

18 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Example 3​: Create parent child category using custom app.

Create custom doc type "Parent Child Category" with below fields and settings:

Category Name Data category_name


Parent ID Link parent_id Options(Parent Child Category)

View setting :
Title: category_name

Create custom two custom fields in Item (Customize):

Parent Category Link parent_category Options(Parent Child Category)


Child Category Link child_category Options(Parent Child Category)

Get child category based on select parent category.

Create custom.js file: Path: "public/js/custom.js"

parent category filter:

frappe.ui.form.on('Item',{
onload: function(frm) {
console.log("par::::::::",frm.doc);

cur_frm.set_query("parent_category", function() {
console.log("dddddddddddddddddddddd",frm.doc);
return {
"filters": {
"parent_id": ("=", "")
}
};
});
}
});

child category filter:

19 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

frappe.ui.form.on('Item',{
"parent_category": function(frm) {
console.log("frm::::::::",frm.doc);

cur_frm.set_query("child_category", function() {
return {
"filters": {
"parent_id": frm.doc.parent_category
}
};
});
}
});

Hook.py:

doctype_js = {
"Item": "public/js/custom.js",
}

20 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Screen Shot:

21 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Example 4: Customize customer status using custom app.

Add custom fields: Doctype: Customer


-----------------------------------------------------

Status Select Options(Open,Close,Pending,Blocked)

Menu > Customize > Update button > Export Customizations

Create custom.js file in custom app.


Path: "public/js/custom.js"

custom.js:

frappe.listview_settings['Customer'] = {
get_indicator:function(doc){
if (doc.status === "Open") {
return [__("Open"), "green", "status,=,Opened"];
}
if (doc.status === "Close") {
return [__("Close"), "red", "status,=,Closed"];
}
}
}

hook.py:

doctype_list_js = {"Customer" : "public/js/custom.js"}

22 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Screen Shot:

23 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Example 5: Frappe.get_doc using custom app.

Get_doc by PY (Client side)

frappe.get_doc(doctype, name)
e.g. frappe.get_doc('Customer', 'self.name')

Load a document from the database with give doctype (table) and name Returns a Document
object. All columns are properties. e.g. doc.name

custom.py method:

def call_customer_doc_object(self,cdt):
print ("\n CALL DOC ID:::::::::::::::",self.name)

doc = frappe.get_doc('Customer',self.name)
print ("\n CALL DOC OBJECT::::::::::::::::::",doc)
print ("\n customer_name::::::::::::::::::",doc.customer_name)
print ("\n customer_type::::::::::::::::::",doc.customer_type)
print ("\n customer_group::::::::::::::::::",doc.customer_group)

hooks.py file:

doc_events = {
"Customer" :{
"validate" :
"sample_demo.sample_demo.doctype.parent_child_category.parent_child_category.call_custo
mer_doc_object",
}
}

24 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Example 6: Frappe call method using custom app

In Frappe Framework, you can manage ajax calls via frappe.call. The frappe.call works in
asynchronous manner ie. send requests and handle response via callback mechanism.

frappe.call Structure:

frappe.call({

type: opts.type || "POST",

args: args,

success: callback,

error: opts.error,

always: opts.always,

btn: opts.btn,

freeze: opts.freeze,

freeze_message: opts.freeze_message,

async: opts.async,

url: opts.url || frappe.request.url,

})

25 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Parameter description :

type: String parameter, http request type "GET", "POST", "PUT", "DELETE". Default set to
"POST".

args: associative array, arguments that will pass with request.

success: Function parameter, code snippet, will after successful execution of request

error: Function parameter, code snippet, will execute after request failure

always: Function parameter, code snipper, will execute in either case

btn: Object parameter, triggering object

freeze: Boolean parameter, if set freeze the instance util it receives response

freeze_message: String parameter, message will populate to screen while screen is in freeze
state.

async: Boolean parameter, default set to true. So each frappe.call is asynchronous. To make
call synchronous set parameter value as false

url: String parameter, location from where hitting the request

26 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Code at client side:


frappe.ui.form.on('Ebook Example',{
custom_print_date: function(frm) {
console.log("::::::call method::::::::::", frm);
frappe.call({
method:
"sample_demo.sample_demo.doctype.ebook_example.ebook_example.custom_print_date",
args: {
date: frappe.datetime.nowdate(),
},
callback: function(r) {
console.log("::::::r.message::::::::::",r.message);
if(r.message) {
frm.set_value("remarks", r.message);
}
}
});
}
});

Code at server side:


# Python whitelist method
# ---------------------------------

@frappe.whitelist()
def custom_print_date(date=None):
print ("\n custom_print_date method:::::::::::::::::")
date = date or today()
return date

Hook.py:
# Define custom js
#-----------------------
doctype_js = {
"Ebook Example": "public/js/custom.js",
}

27 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Screen Shot:

28 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Example 7: Creating app icon in desk

You will now see an icon for the Sample Demo module. If you do not see an icon you will have
to configure the desk. So go to the config folder (of the newly made app) and create a new file
sample_demo.py.

Directory Structure
apps/
├── frappe
└── sample_demo
├── MANIFEST.in
├── README.md
├── sample_demo
│ ├── __init__.py
│ ├── config
│ │ ├── __init__.py
│ │ ├── desktop.py
│ │ ├── docs.py
│ │ └── ​sample_demo.py <--Here
│ ├── hooks.py
│ ├── sample_demo
│ │ ├── __init__.py
│ │ └── doctype
│ ├── modules.txt
│ ├── patches.txt
│ ├── public
│ ├── templates
│ └── www

Paste the following code to configure the desk in order to view the Module.

29 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

from __future__ import unicode_literals


import frappe
from frappe import _

def get_data():
return[
{
"label": ("ERPNext Example"),
"items": [
{
"type": "doctype",
"name": "Ebook Example",
"onboard": 1,
"label": _("ERNext EBook Example"),
"description": _("ERPNext Tutorials Technical Guide-2020"),
}
]
}
]

Now save the script and reload the page. You should see the icon for the sample demo module.
Click on that icon and you will see the Module page:

30 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Example 8: Script Report by using custom app

You can create tabulated reports using server side scripts by creating a new Report.

1. Goto developer > click on "Report" > Create a new Report

1) Goto Developer menu > Click on Report menu


2) Fill report details : Report Name, Report Type: Report Builder,Ref DocType,Is Standard: Yes,
Module
3) Save
4) Make code py and js file accordingly in our report directory under the custom app

31 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

2 In the .py file you can add the script for generating the report.

1) In the execute method, two lists columns and data are returned
2) Columns must be a list of dictionaries containing fields like fieldname, label, fieldtype,
options,width. For example:

custom_script_report.py file :

# Copyright (c) 2013, Solufy and contributors


# For license information, please see license.txt

from __future__ import unicode_literals


import frappe
from frappe import _

def execute(filters=None):
columns = get_report_columns()
data = get_report_data(filters)
return columns, data

def get_report_columns():
columns = [{
"fieldname": "name1",
"label": _("Name"),
"fieldtype": "Data",
"options": "",
"width": 200
},
{
"fieldname": "address",
"label": _("Address"),
"fieldtype": "Data",
"width": 200
},
]
return columns

def get_report_data(filters=None):
data = get_orders(filters)

32 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

return data

def get_orders(filters):
#additional_conditions = get_additional_report_conditions(filters)
test_q = """select name1, address from `tabEbook Example`"""
return frappe.db.sql(test_q, as_dict=True)

3. Add link for your report on the module page

In the module folder (for example if it is sample_demo in ERPNext the folder will be
erpnext/sample_demo/config/sample_demo.py) you will see labels and items for various
sections. The new report can be added in the item list as show in the example:

from __future__ import unicode_literals


import frappe
from frappe import _

def get_data():
return[
{
"label": ("ERPNext Example"),
"items": [
{
"type": "doctype",
"name": "Ebook Example",
"onboard": 1,
"label": _("ERNext EBook Example"),
"description": _("ERPNext Tutorials Technical Guide-2020"),
}
]
},
{
"label": ("Reports"),
"items": [
{
"type": "report",
"name": "Custom Script Report",
"doctype": "Ebook Example",
"is_query_report": True
}

33 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

]
},
]

34 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

Example 9: Customization of sale invoice report template using


custom app

1) Created custom app: tax_invoice

2) Customization standard.html file


Path:​ /demo/erpnext/frappe-bench/apps/tax_invoice/tax_invoice/templates/print_formats/
Standard.html

{%- macro add_header(page_num, max_pages, doc, letter_head, no_letterhead) -%}


{% if doc.doctype == 'Sales Invoice' %}
{% if letter_head and not no_letterhead %}
<div class="letter-head">{{ letter_head }}</div>
{% endif %}
{%- if doc.meta.is_submittable and doc.docstatus==0-%}
<!-- <div class="alert alert-info text-center">
<h4 style="margin: 0px;">{{ _("DRAFT") }}</h4></div> -->
{%- endif -%}
{%- if doc.meta.is_submittable and doc.docstatus==2-%}
<!-- <div class="alert alert-danger text-center">
<h4 style="margin: 0px;">{{ _("CANCELLED") }}</h4></div> -->
{%- endif -%}
{% if max_pages > 1 %}
<p class="text-right">{{ _("Page #{0} of {1}").format(page_num, max_pages) }}</p>
{% endif %}
{% endif %}
{%- endmacro -%}

<small>
{{ add_header(0,1,doc,letter_head, no_letterhead) }}

{% if doc.doctype == 'Sales Invoice' %}


<style>
/* table tr {
font-family: "Times New Roman", Times, serif;

35 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

font-size: 14px;
font-weight: normal;
} */
.table{
border: 1px solid black;
color:#000000;
font-size:16px;
}
.tr{
border: 1px solid black;
}

.print-format table, .print-format tr,


.print-format td, .print-format div, .print-format p {
font-family: sans-serif;
font-size: 15px;
line-height: 90%;
vertical-align: middle;
}
@media screen {
.print-format {
width: 210mm;
padding: 10mm;
min-height: 297mm;
footer {page-break-after: always;}
}
}
/* @page {
.print-format {
margin-top: 0.39in;
margin-bottom: 0.39in;
margin-left: 0.39in;
margin-right: 0.39in;
}
} */
</style>

<div>
<table style="height: 36px;" width="100%">
<tbody>

36 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

<tr>
{% if doc.company_address %}
{% set cc = frappe.get_doc("Address", doc.company_address) %}
<td style="width: 292.5px;"><b> {{ doc.company_address }}</b> <br></br><b>GSTIN :</b> {{
cc.gst_state_number }} <br /><b>Address :</b> {{ cc.address_line1 }} <br /><b>Phone :</b>{{
cc.phone }} <br>
<b>Emails :</b> {{ cc.email_id }}</td>
{% endif %}
<td style="width: 292.5px;">&nbsp;</td>
<td style="width: 292.5px;">

<p><strong>GST INVOICE</strong></p><br>
<p><strong>Invoice ID :</strong> {{ doc.name }}&nbsp;</p>
<p><strong>Invoice Date :</strong> {{ doc.po_date }}</p>
<p><strong>Eligible for Reverse GST : </strong> Yes</p>
</td>
</tr>
</tbody>
</table>
</div>

<div><hr /></div>
<div>
<table style="height: 36px;" width="100%" printdata>
<tbody>
<tr>
<td style="width: 292.5px;">&nbsp;<strong>PO ID :</strong> {{ doc.po_no }}</td>
<td style="width: 292.5px;">&nbsp;<strong>PO Date : </strong> {{ doc.po_date }}</td>
<td style="width: 292.5px;">&nbsp;<strong>Payment : </strong>{{ doc.outstanding_amount
}}</td>
</tr>
</tbody>
</table>
</div>
<div><hr /></div>
<div>
<table style="height: 36px;" width="100%">
<tbody>
<tr>
<td style="width: 292.5px;">

37 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

<p>&nbsp;<strong>Billed to: </strong>{{ doc.customer_name }}&nbsp;<br />{{


doc.address_display }}</p>
<p><strong>GSTIN : </strong> {{ doc.customer_gstin }}&nbsp;</p>
{% if doc.customer_address %}
{% set cc = frappe.get_doc("Address", doc.customer_address) %}
<p><strong>State: </strong> {{ cc.gst_state_number }} &nbsp;</p>
{% endif %}

</td>
<td style="width: 292.5px;">&nbsp;</td>
<td style="width: 292.5px;">
<p><strong>Shipped to: </strong>{{ doc.shipping_address_name }}</p>
<p><strong>Shipping Mode:</strong></p>
<p><strong>Vehicle No.: </strong></p>
<p><strong> Place of Supply:</strong>{{ doc.place_of_supply }}</p>
</td>
</tr>
</tbody>
</table>
</div>

<div>

<table class="table table-condensed table-hover table-bordered">


<tr>
<th>Sr</th>
<th>Description</th>
<th class="text-right">Qty</th>
<th class="text-right">UOM</th>
<th class="text-right">Desc %</th>
<th class="text-right">Net Rate</th>
<th class="text-right">Amount</th>
</tr>
{%- for row in doc.items -%}
<tr>
<td style="width: 3%;">{{ row.idx }}</td>
<td style="width: 57%;">{{ row.description }}</td>
<td style="width: 10%; text-align: right;">{{ row.qty }}</td>
<td style="width: 10%; text-align: right;">{{ row.uom }}</td>
<td style="width: 10%; text-align: right;">{{ row.discount_percentage }}</td>
<td style="width: 15%; text-align: right;">{{ row.rate }}</td>
<td style="width: 15%; text-align: right;">{{ row.amount }}</td>

38 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

</tr>
{%- endfor -%}
</tbody>
</table>
</div>

<table class="table table-condensed table-hover table-bordered">


<tr>
<td style="width: 50%;">
</td>
<td style="width: 50%;text-align:right;">
<b>{{ _("Sub Total") }}:&nbsp;&nbsp;</b> {{ doc.net_total }}<br>
</td>
</tr>
<tr>
<td style="width: 50%;">
<p></p>
</td>
</tr>
<tr>
<td style="width: 50%;">
<b>{{ _("In Words") }}:&nbsp;&nbsp;</b> {{ doc.in_words }}<br>
</td>
<td style="width: 50%;text-align:right;">

<b>{{ _("Grand Total") }}:</b> {{ doc.grand_total }}<br>


</td>
</tr>
</table>

<div>
<table width="100%">
<tbody>
<tr>
<td style="width: 292.5px;" colspan="3"><strong>Tax Breakup:</strong>
<table class="table table-condensed table-hover table-bordered" width="100%">
<tbody>

39 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

<tr style="height: 21.2266px;">


<td style="width: 173.75px; height: 20px;">{{ doc.other_charges_calculation }}&nbsp;</td>
</tr>

</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>

<div>
<td>
</td>
</div>

<div>
<table style="height: 36px;" width="100%">
<tbody>
<tr>
<td> {{ doc.terms }}</td>
</tr>
</tbody>
</table>
</div>
<div>

<p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p>

<table style="height: 36px;" width="100%">


<tbody>
<tr>
<td style="width: 292.5px;" align="left">
<p>Received goods in Good Condition</p>
<p>Name, Mobile &amp; Signature of the receiver.</p>
</td>
<td style="width: 292.5px;" align="center">
<p><strong>THANK YOU <strong></p>
<p>for being our Customer.</p>
<p>Look forward to serve you again.</p>
</td>

40 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

<td style="width: 292.5px;" align="right">Company Stamp &amp;


Signature&nbsp;</td>
</tr>
</tbody>
</table>
</div>

<p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p>

<div>
<table style="height: 36px;" width="100%">
<tbody>
<tr>
<td style="width: 292.5px;" align="left">&nbsp;<strong>GST Invoice: </strong>{{ doc.name
}}&nbsp;</td>
<td style="width: 292.5px;" align="center">&nbsp;NB : Company Stamp or Signature not
required, if this document has received by email. <br />Dated: {{ doc.get_formatted }}</td>
<td style="width: 292.5px;" align="right">&nbsp;<strong> Page No: 1 to 1 </strong></td>
</tr>
</tbody>
</table>
</div>

{% endif %}

3) Override standard.html file hook.py > custom app

override_whitelisted_methods = {
"Frappe.templates.print_formats.standard":
"tax_invoice.tax_invoice.templates.print_formats.standard",

41 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

4) Screen shot

42 contact@solufy.in
ERPNext 2​nd​ Edition Ebook

References:
1) https://frappe.io/docs/user/en/tutorial

43 contact@solufy.in

You might also like