Single DocType is a special type of DocType that stores only one record. It is useful for storing settings or configuration values that are global to the application. The data is stored in a single row in the database, and the form is always available for editing.
Library Settings Controller
Now, let’s write code to use the Library Settings in our validations. We will
update the Library Transaction validation to check the loan period and
maximum number of issued articles.
library_transaction.py
from frappe.model.document import Document
from frappe.model.docstatus import DocStatus
import frappe
class LibraryMembership(Document):
# check before submitting this document
def before_submit(self):
exists = frappe.db.exists(
"Library Membership",
{
"library_member": self.library_member,
"docstatus": DocStatus.submitted(),
# check if the membership's end date is later than this membership's start date
"to_date": (">", self.from_date),
},
)
if exists:
frappe.throw("There is an active membership for this member")
# get loan period and compute to_date by adding loan_period to from_date
loan_period = frappe.db.get_single_value("Library Settings", "loan_period")
self.to_date = frappe.utils.add_days(self.from_date, loan_period or 30)
We used the tabSingles method to get the Library Settings record. Since it
is a Single DocType, we can directly access its fields. We also used thefrappe.db.get_single_value method to count the number of issued articles for the member.
Library Membership Validation Update
We also need to update the Library Membership validation to use the Library
Settings for the loan period.
library_membership.py
import frappe
from frappe.model.document import Document
from frappe.model.docstatus import DocStatus
class LibraryTransaction(Document):
def before_submit(self):
if self.type == "Issue":
self.validate_issue()
self.validate_maximum_limit()
# set the article status to be Issued
article = frappe.get_doc("Article", self.article)
article.status = "Issued"
article.save()
elif self.type == "Return":
self.validate_return()
# set the article status to be Available
article = frappe.get_doc("Article", self.article)
article.status = "Available"
article.save()
def validate_issue(self):
self.validate_membership()
article = frappe.get_doc("Article", self.article)
# article cannot be issued if it is already issued
if article.status == "Issued":
frappe.throw("Article is already issued by another member")
def validate_return(self):
article = frappe.get_doc("Article", self.article)
# article cannot be returned if it is not issued first
if article.status == "Available":
frappe.throw("Article cannot be returned without being issued first")
def validate_maximum_limit(self):
max_articles = frappe.db.get_single_value("Library Settings", "max_articles")
count = frappe.db.count(
"Library Transaction",
{
"library_member": self.library_member,
"type": "Issue",
"docstatus": DocStatus.submitted(),
},
)
if count >= max_articles:
frappe.throw("Maximum limit reached for issuing articles")
def validate_membership(self):
# check if a valid membership exist for this library member
valid_membership = frappe.db.exists(
"Library Membership",
{
"library_member": self.library_member,
"docstatus": DocStatus.submitted(),
"from_date": ("<", self.date),
"to_date": (">", self.date),
},
)
if not valid_membership:
frappe.throw("The member does not have a valid membership")
Now, when you create a Library Membership, the loan period will be automatically
set based on the Library Settings.
Summary
In this tutorial, we learned about different types of doctypes in the framework:
- Master and Transactional DocTypes
- Submittable DocTypes
- Single DocTypes
We also learned how to use Link fields to connect records, fetch values from
linked records, and write validations for our doctypes.