1. Project Overview & Quickstart (frappe/books)
File: README.md
Frappe Books
Frappe Books is an open-source accounting software aimed at simplifying financial management for businesses. With its clean and user-friendly interface, it streamlines accounting tasks for small and medium-sized enterprises, offering a seamless solution for modern businesses to manage their finances with ease.
Screenshots
Motivation
Frappe Books addresses a market gap where small businesses face expensive, complex accounting tools. It offers an intuitive, open-source solution that combines simplicity with essential features, empowering businesses to manage finances effectivelyβeven offline.
Key Features
- Dashboard: Provides an overview of key financial data and performance metrics.
- Point of Sale: Simplifies retail transactions with an integrated POS system for easy sales processing.
- Works Offline: Enables users to continue working without an internet connection and sync later.
- Double-entry accounting: Ensures accurate financial tracking by recording each transaction in two accounts.
- Entries
- Invoicing: Allows businesses to create and manage professional invoices effortlessly.
- Billing: Billing processes by generating bills and tracking payments.
- Payments: Records and tracks payments received and made.
- Journal Entries: Records financial transactions in the general ledger with detailed notes and adjustments.
- Financial Reports
- General Ledger: Centralized record of all financial transactions, providing a comprehensive view of accounts.
- Profit and Loss Statement: Summarizes revenues, costs, and expenses to show business profitability.
- Balance Sheet: Displays a companyβs assets, liabilities, and equity at a specific point in time.
- Trial Balance: Verifies the accuracy of accounting records by ensuring that debits and credits are balanced.
Under the Hood
Vue.js: In Frappe Books, Vue.js powers the front-end, enabling a reactive and component-based UI. It ensures seamless interactions and dynamic updates, giving users a modern, responsive experience.
Electron: Electron is used to package Frappe Books as a standalone desktop application, allowing it to run offline and provide a native-like experience across Windows, macOS, and Linux.
SQLite: Frappe Books uses SQLite as its local database. All financial data, transactions, and configurations are stored securely in an SQLite file on the user's machine.
Production Setup
Manual
Download and install the latest release for your platform from the releases
page .
Using Homebrew (for MacOS and Linux)
brew install --cask frappe-booksVia Flatpak (Linux)
Development Setup
Pre-requisites
To get the dev environment up and running you need to first set up Node.js v20.18.1 and npm. For this, we suggest using
nvm.
Next, you will need to install yarn.
Clone and Run
Once you are through the Pre-requisites, you can run the following commands to
setup Frappe Books for development and building:
# clone the repository
git clone https://github.com/frappe/books.git
# change directory
cd books
# install dependencies
yarnTo run Frappe Books in development mode (with hot reload, etc):
# start the electron app
yarn devNote: First Boot
When you run yarn dev electron will run immediately but the UI will take a
couple of seconds to render this because of how dev mode works. Each file is
individually served by the dev server. And there are many files that have to be
sent.
Note: Debug Electron Main Process
When in dev mode electron runs with the --inspect flag which allows an
external debugger to connect to port 5858. You can use chrome for this by
visiting chrome://inspect while Frappe Books is running in dev mode.
See more here.
Build
To build Frappe Books and create an installer:
# start the electron app
yarn buildNote: Build Target
By default the above command will build for your computer's operating system and
architecture. To build for other environments (example: for linux from a windows
computer) check the Building section at
electron.build/cli.
So to build for linux you could use the --linux flag like so: yarn build --linux.
Want to Just Try Out or Contribute?
If you want to contribute to Frappe Books, please check our Contribution Guidelines. There are many ways you can contribute even if you don't code:
- If you find any issues, no matter how small (even typos), you can raise an issue to inform us.
- You can help us with language support by contributing translations.
- If you're an ardent user you can tell us what you would like to see.
- If you have accounting requirements, you can become an ardent user. π
If you want to contribute code then you can fork this repo, make changes and raise a PR. (see how to)
Translation Contributors
| Language | Contributors |
|---|---|
| Arabic | taha2002, Faridget, Osama Muhammed |
| Albanian | xoniks |
| Catalan | DΓdac E. JimΓ©nez |
| Chinese - Simplified | wcxu21, wolone, Ji Qu |
| Chinese - Traditional | Ethan Deng |
| Danish | Tummas Joensen |
| Dutch | RijckAlex, Stan M |
| French | DeepL, mael-chouteau, joandreux |
| German | DeepL, barredterra, promexio, C2H6-383, 0xflotus, Tim |
| Gujarati | dhruvilxcode, 4silvertooth |
| Hindi | bnsinghgit |
| Indonesian | Aji Prakoso |
| Korean | Isaac-Kwon |
| Portuguese | DeepL, Valdir Amaral |
| Spanish | talmax1124, delbertf, Ignacio Chemes |
| Swedish | papplo, Crims-on |
| Turkish | Eyuq, XTechnology-TR |
Learn and connect
- Telegram Group: Used for discussions and decisions regarding everything Frappe Books.
- GitHub Discussions: Used for discussions around a specific topic.
- Documentation: Official documentation for more details.
File: dummy/README.md
Dummy
This will be used to generate dummy data for the purposes of tests and to create
a demo instance.
There are a few .json files here (eg: items.json) which have been generated,
these are not to be edited.
The generated data has some randomness, there is a baseCount arg to the
exported setupDummyInstance function, the number of transactions generated are
always more than this.
File: fyo/README.md
Fyo
This is the underlying framework that runs Books, at some point it may be
removed into a separate repo, but as of now it's in gestation.
The reason for maintaining a framework is to allow for varied backends.
Currently Books runs on the electron renderer process and all db stuff happens
on the electron main process which has access to nodelibs. As the development
of Fyo progresses it will allow for a browser frontend and a node server
backend.
This platform variablity will be handled by code in the fyo/demux subdirectory.
Pre Req
Singleton: The Fyo class is used as a singleton throughout Books, this
allows for a single source of truth and a common interface to access different
modules such as db, doc an auth.
Localization: Since Books' functionality changes depending on region,
regional information (countryCode) is required in the initialization process.
Doc: This is fyo's abstraction for an ORM, the associated files are
located in model/doc.ts, all classes exported from books/models extend this.
Terminology
- Schema: object that defines shape of the data in the database.
- Model: the controller class that extends the
Docclass, or theDoc
class itself (if a specific controller doesn't exist). - doc (not
Doc): instance of a Model, i.e. what has the data.
If you are confused, I understand.
Initialization
There are a set of core models which are maintained in the fyo/models
subdirectory, from this the SystemSettings field countryCode is used to
config regional information.
A few things have to be done on initialization:
1. Connect To DB
If creating a new instance then fyo.db.createNewDatabase or if loading an
instance fyo.db.connectToDatabase.
Both of them take countryCode as an argument, fyo.db.createNewDatabase
should be passed the countryCode as the schemas are built on the basis of
this.
2. Initialize and Register
Done using fyo.initializeAndRegister after a database is connected, this should be
passed the models and regional models.
This sets the schemas and associated models on the fyo object along with a few
other things.
Sequence
First Load: i.e. registering or creating a new instance.
- Get
countryCodefrom the setup wizard. - Create a new DB using
fyo.db.createNewDatabasewith thecountryCode. - Get models and
regionalModelsusingcountryCodefrommodels/index.ts/getRegionalModels. - Call
fyo.initializeAndRegisterwith the all models.
Next Load: i.e. logging in or opening an existing instance.
- Connect to DB using
fyo.db.connectToDatabaseand getcountryCodefrom the return. - Get models and
regionalModelsusingcountryCodefrommodels/index.ts/getRegionalModels. - Call
fyo.initializeAndRegisterwith the all models.
Note: since SystemSettings are initialized on fyo.initializeAndRegister
db needs to be set first else an error will be thrown
Testing
For testing the fyo class, mocha is used (node side). So for this the
demux classes are directly replaced by node side managers such asDatabaseManager.
For this to work the class signatures of the demux class and the manager have to
be the same which is maintained by abstract demux classes.
DatabaseManager is used as the DatabaseDemux for testing without API or IPC
calls. For AuthDemux the DummyAuthDemux class is used.
Translations
All translations take place during runtime, for translations to work, aLanguageMap (for def check utils/types.ts) has to be set.
This can be done using fyo/utils/translation.ts/setLanguageMapOnTranslationString.
Since translations are runtime, if the code is evaluated before the language map
is loaded, translations won't work. To prevent this, don't maintain translation
strings globally since this will be evaluated before the map is loaded.
Observers
The doc and db handlers have observers (instances of Observable) as
properties, these can be accessed using
fyo.db.observerfyo.doc.observer
The purpose of the observer is to trigger registered callbacks when some doc
operation or db operation takes place.
These are schema level observers i.e. they are registered like so:method:schemaName. The callbacks receive args passed to the functions.
File: models/README.md
Models
The models root folder contains all the model files, i.e. files containing all
the models. Models here, refers to the classes that handle the data, its
validation and updation and a bunch of other stuff.
Each model directly or indirectly extends the Doc class fromfyo/model/doc.ts so for more info check that file and the associated types
in fyo/model/types.ts.
A model class can used even if the class body has no content, for examplePurchaseInvoiceItem. Else the model used will default to using Doc. The
class can also be used to provide type information for the field types else they
default to the catch all DocValue example:
class Todo extends Doc {
title?: string;
date?: Date;
completed?: boolean;
}While this has obvious advantages, the drawback is if the underlying fieldtype
changes this too will have to be changed.
The data stored by the models is decided by the schema passed to it's
constructor. Check schemas/README.md for info on this.
Adding Stuff
When adding stuff to models/** make sure that it isn't importing any Vue code
or other frontend specific code globally. This is cause model file tests will
directly use the the Fyo class and will be run using mocha on node.
Importing frontend code will break all the tests. This also implies that one
should be wary about transitive dependencies.
It should also not import the Fyo object (singleton) from src, where everfyo is required in models it should be passed to it.
Note: Frontend specific code can be imported but they should be done so, only
using dynamic imports i.e. await import('...').
Regional Models
Regional models should as far as possible extend the base model and override
what's required.
They should then be imported dynamicall and returned from getRegionalModels inmodels/index.ts on the basis of countryCode.
File: reports/README.md
Reports
Reports are a view of stored data, the code here doesn't alter any data.
All reports should extend the Report class in reports/Report.ts, depending
on the report it may have custom .vue files. Check the type.ts file for the
shape of the report data.
File: schemas/README.md
Schema
Main purpose of this is to describe the shape of the models' table in the
database. But there is some irrelevant information in the schemas with
respect to this goal. This is information is allowed as long as it is not
dynamic, which is impossible anyways as the files are data (.json, not .js)
If any field has to have a dynamic value, it should be added to the controller
file by the same name, check the books/models subdirectory for this.
There are a few types of schemas:
- Regional: Schemas that are in the '../regional' subdirectories
these can be of any of the below types. - Abstract: Schemas that are not used as they are but only after they are
extended by Stub schemas. Indentified by theisAbstractfield - Subclass: Schemas that have an
"extends"field on them, the value of which
points to an Abstract schema. - Complete: Schemas which are neither abstract nor stub.
For more detail on the meta structure of the schema check books/schemas/types.ts.
Final Schema
This is the schema which is used by the database and app code and is built by
combining the above types of schemas.
The order in which a schema is built is:
- Build Regional schemas by overriding the fields and other properties of the
non regional variants. - Combine Subclass schemas with Abstract schemas to get complete schemas.
Note: if a Regional schema is not present as a non regional
variant it's used as it is.
Additional Notes
In all the schemas, the "name" field/column is the primary key. If it isn't
explicitly added, the schema builder will add it in.
The following schema fields will be implicitly translated by the frontend:"label", "description", and "placeholder", irrespective of nesting.
File: src/README.md
src
This is where all the frontend code lives
Fyo Initialization
The initialization flows are different when the instance is new or is existing.
All of them are triggered from src/App.vue.
New Instance
- Run Setup Wizard for init values (eg:
country). - Call
setupInstance.ts/setupInstanceusing init values.
Existing Instance
- Connect to db.
- Check if Setup Wizard has been completed, if not, jump to New Instance
- Call
initFyo/initializeInstancewithdbPathandcountryCode
Global Fyo
Global fyo is exported from initFyo.ts. Only code that isn't going to be unit
tested using mocha should use this, i.e. code in src
2. Official Technical Reference & Guides (frappe/books_docs)
File: docs/entries/entries.md
Entries
Besides Transactional Entries Frappe
Books has regular entries for documents that don't affect any account's balance
directly.
Examples of these are:
Creating a New Entry
To create an entry navigate to it's page first, for example for
Taxes: Setup > Taxes
And click on the blue + button, this will open the Quick Edit Form.
Enter the values and click the blue Save button. The entry will now be made and
can be used in transactions.
Duplicating an Entry
To create the duplicate of an entry, click on the ... menu button and select
Duplicate.
This will create a Draft entry, i.e. it is not yet saved, you can change the
values and click on Save.
After the entry has been Saved you can use it in transactions.
Deleting an Entry
To delete an entry, click on the ... menu button and select Delete.
::: warning Entry Deletion
Entries used in transactions cannot be deleted.
To delete these entries the Transactions where they are used will have to be
deleted first.
:::
Now that you know the basics of entries you can learn about specific types of
entries.
File: docs/entries/items.md
Items
Item is term used by Frappe Books for anything that is purchased or sold using
a Sales or Purchase Invoice.
Types of Items
An Item is classified by what purpose it's used for.
| # | For | Name | Usage | Navigation |
|---|---|---|---|---|
| 1 | Sales | Sales Items | Sales Invoices | Sales > Sales Items |
| 2 | Purchases | Purchase Items | Purchase Invoices | Purchases > Purchase Items |
| 3 | Both | Common Items | Sales and Purchase Invoices | Common > Common Items |
::: warning Usage
Make sure that you create the Item under the correct Navigation as this cannot
be changed after creation.
:::
Item Fields
When creating a new Item the following fields are displayed:
- Item Image: an image to identify the Item by.
- Item Name: a name to identify the Item by, each Item should have a unique name.
- Rate: base cost of an Item, this can be changed in the invoice.
- Unit Type: base unit of the Item to which the rate is applied
- Type: indicates whether the Item is a Product or a Service.
- For: the purpose of the Item, whether it's for Sales, Purchases or Both.
- Tax: tax applied on the Item when making an invoice, this can be changed in the invoice.
- Description: textual description of what the Item is.
- Income: the income account, account that is credited when the Item is sold.
- Expense: the expense account, account that is debited when the Item is purchased.
- HSN/SAC: the HSN/SAC code that is used for classifying Items.
File: docs/entries/party.md
Party
Party is a term used in Frappe Books for any entity to whom a Sale or from
whom a Purchase is made.
Types of Party
A Party is classified by their Role it's used for.
| # | Role | Name | Invoice | Navigation |
|---|---|---|---|---|
| 1 | Customer | Customer | Sales Invoices | Sales > Customers |
| 2 | Supplier | Supplier | Purchase Invoices | Purchases > Suppliers |
| 3 | Both | Party | Sales and Purchase Invoices | Common > Party |
::: warning Usage
Make sure that you create the Party under the correct Navigation as this cannot
be changed after creation.
:::
Party Fields
- Party Image: an image to identify the Party by.
- Party Name: a name to identify the Party by, each Party should have a unique name.
- Email: the Party's email address that may be displayed when printing the invoice.
- Phone: the Party's phone number that may be displayed when printing the invoice.
- Address: the Party's address that may be displayed when printing the invoice.
- Default Account: default account that may be credited or debited when a
transaction involving the part is made. - Currency: the currency in which to create the invoice for the Party.
- Role: the role of the Party, whether they are a Customer, Supplier or Both.
Recent Invoices
The Party Quick Edit Form has a Recent Invoices widget. This shows the last
three invoices made for a Party.
Each row shows the following information
- Invoice Name
- Invoice Status
- Date
- Paid Amount
- Outstanding Amount
Clicking on a row will take you to the Invoice from where Payments can be made.
File: docs/entries/price-list.md
Price List
Using Price List, you can keep track of multiple Item Prices for sales and purchase.
An Item can by priced differently depending on whether it is for sale or
purchase, if it is discount season, etc.
A Price List helps by grouping multiple item prices under a single entry. You
can then select this entry when making a Sales or a Purchase Invoice, and when
an item is selected its rate will be fetched from the Price list.
To use Price Lists you need to first enable them.
Enable Price List
The Price List feature needs to be enabled first by checking the
Enable Price List option in the General Settings.
To navigate: Setup > Settings > General Tab
Create a Price List
To create a Price Lists, go to:Common > Price List. And then click on the blue+ button to open a new Price List creation form.
Add Item Prices to your price list, when done click on the Save button.
Price List Fields:
- Is Price List Enabled: enables the Price List, if disabled it cannot be used.
- For Sales: if enabled, the Price List can be selected in Sales Invoices.
- For Purchases: if enabled, the Price List can be selected in Purchase Invoices.
:::tip Multiple Prices
You can add multiple prices for an Item in the Price List. When fetching the
price from the Price List, the first Item Price with a matching Unit Type will
be selected.
:::
Setting a Price List
To use a Price List, ou need to select Price List in the Sales or Purchase Invoice form.
Once this is done, when a new item is selected it's price will be fetched from
the selected Price List.
File: docs/entries/taxes.md
Taxes
To calculate the amount of tax on an Item's purchase or sale, a Tax entry has to
be selected on the invoice row.
::: tip Auto Set Tax
If the Tax has been set on the Item then it will be auto set in the invoice row.
:::
Tax entries can be made from the Tax page: Setup > Taxes
Tax Fields
A Tax entry has just two fields
- Tax Name: a unique name to identify the tax by.
- Tax Detail: a table where the Tax account along with the Tax rate is set.
::: info Tax Detail
Tax Detail is a table as a single tax can have multiple components each with a
separate account and rate.
:::
File: docs/basics/accounting-basics.md
Accounting Basics
A small primer to get you up to speed on the concepts of accounting.
Let's understand the concept of accounting with the example of an imaginary
business that is just starting out.
Owner Investment: Letβs say weβre starting a Home Interior business and we have βΉ5,00,000 of
capital to start our business.
Bank balance will initially be βΉ5,00,000.
First Expense: We will need an operating place where we can work and meet clients. For
this, we rent out a co-working space for βΉ40,000 per month. This is our first
expense.
Bank balance will now be βΉ5,00,000 - βΉ40,000 = βΉ4,60,000.
First Income: We have invested some cash, and we have a working place. Now
we make our first sale having a value of βΉ30,000. We earned βΉ30,000 from our
first customer. This is our first income.
Bank balance at this point will be βΉ4,60,000 + βΉ30,000 = βΉ4,90,000.
Accounts Payable: Finally, Letβs say we decided to hire a Web Agency to build a website for us,
they charge βΉ50,000 which will be paid after 2 months from current date.
At this point we havenβt paid them any money, but we are liable to pay them in
the future. This is a liability and will be recorded under Accounts Payable.
Events and Accounts
Letβs list these events down in a table:
| # | Event | Amount |
|---|---|---|
| 1 | Owner Investment | + βΉ5,00,000 |
| 2 | Rent | - βΉ40,000 |
| 3 | Sales | + βΉ30,000 |
| 4 | Accounts Payable | - βΉ50,000 |
| 5 | Total | βΉ4,40,000 |
Now, let's categorize these into the five types of Accounts and list their final
balances:
| # | Account | Amount |
|---|---|---|
| 1 | Asset (Bank) | βΉ4,90,000 |
| 2 | Liabilities (Accounts Payable) | βΉ50,000 |
| 3 | Equity (Owner Investment) | βΉ5,00,000 |
| 4 | Income (Sales) | βΉ30,000 |
| 5 | Expense (Rent) | βΉ40,000 |
Impact on Accounts
Letβs take a look at each transaction and understand which accounts will get
affected, from an accounting point-of-view:
1. Cash Investment
Since you, the owner, invested money into the business, it is a liability.
So, Accounts Payable, which is a Liability account, is increased by
βΉ5,00,000.
Also, the money in the Bank Account, which is an Asset account, is increased
by βΉ5,00,000.
| Account | Credit | Debit |
|---|---|---|
| Accounts Payable | βΉ5,00,000 | 0 |
| Bank Account | 0 | βΉ5,00,000 |
2. Rent
Rent is an immediate expense, so it is recorded in the Rent Account, which is
an Expense account, it is increased by βΉ40,000.
Also, the money is spent from the Bank Account, which is an Asset account, it
is decreased by βΉ40,000.
| Account | Credit | Debit |
|---|---|---|
| Rent Account | 0 | βΉ40,000 |
| Bank Account | βΉ40,000 | 0 |
3. First Sale
We got an immediate payment, it is recorded in Sales Account, which is an
Income account, the balance is increased by βΉ30,000.
Also, we received the money in our Bank Account directly, so itβs balance
increases by βΉ30,000.
| Account | Credit | Debit |
|---|---|---|
| Income Account | βΉ30,000 | 0 |
| Bank Account | 0 | βΉ30,000 |
4. Hiring a Web Agency
We hired a Web Agency for some work, and we are liable to pay them in the
future. This is recorded in Accounts Payable by increasing the amount by
βΉ50,000.
Since this is an expense for us, we also increase the balance in the Vendor
Expense Account by βΉ50,000.
| Account | Credit | Debit |
|---|---|---|
| Accounts Payable | βΉ50,000 | 0 |
| Expense Account | 0 | βΉ50,000 |
As you might have noticed, every transaction we do in our business affects two
accounts. This is the basis of double-entry bookkeeping system.
You can test this out by creating a Sales Invoice in Frappe Books, and then
check the General Ledger Report to see which accounts were affected.
Credit and Debit
The words Credit and Debit mean different things when applied to different
accounts.
For Assets and Expense accounts Credit implies a decrease in balance and Debit
implies an increase in balance.For Liabilities and Income accounts Credit implies an increase in balance and Debit
implies and decrease in balance.
::: tip AEDLIC
You can remember this by the abbreviation AEDLIC which would stand for:
Assets and Expenses Debit, Liabilities and Income Credit
:::
File: docs/basics/introduction.md
Introduction
Welcome to the Frappe Books User Guide. This guide will teach you how to look
after your accounting in Frappe Books. We will go through the basics thatβll
equip you to use the product. However, knowing the basics of accounting is
recommended.
Basics and Setup
Learn the basics of accounting and get started with Frappe Books.
[Accounting Basics](/basics/accounting-basics)
A small primer to get you up to speed on the concepts of accounting.
[Getting Started](/setting-up/getting-started)
Learn how to configure Frappe Books for your company to get started.
Transactions
Learn about the various transactions that you can do in Frappe Books.
[Sales Invoices](/transactions/sales-invoices)
Sales Invoices are bills that are sent to your customers and income is booked.
[Purchase Invoices](/transactions/purchase-invoices)
Purchase Invoices are bills that your supplier sends you for products or services you have purchased.
[Journal Entries](/transactions/journal-entries)
Use journal entries to record transactions that can't be categorized as a sale or purchase.
Analytics and Reports
Learn about the available analytics and reports in Frappe Books
[Dashboard](/analytics/dashboard)
Dashboards show an overview of cashflow, profit or loss and expenses.
[Accounting Reports](/analytics/reports)
Reports include Profit and Loss Statement, Balance Sheet and General Ledger.
File: docs/analytics/balance-sheet.md
Balance Sheet
A Balance Sheet is the financial statement of a company which states assets,
liabilities and equity at a particular point in time.
In Frappe Books, you can view the balance sheet for your accounts based on
monthly, quarterly, half-yearly, and yearly periods.
Navigate to this report from the sidebar: Reports > Balance Sheet
Filters
The Balance Sheet report has several filters which can help you narrow the date
range between which to display the report.
Based On
This allows for toggling betwee the kind of filters used set the date range. It has 2 values:
- Until Date
- Fiscal Year
Periodicity
This selects the interval for which the Balance Sheet values have to be
calculated.
Example setting this to Monthly will display columns on a monthly basis.
Date Range Filters
These filters depend on the value of Based On
| Based On | Date Range Filters |
|---|---|
| Until Date | To Date and Count |
| Fiscal Year | To Year and From Year |
To Date and Count
To Date is the final date until which the report is to be displayed, by default
the values is tomorrow's date.
Count depends on Periodicity so if Count is 3 and Periodicity is Monthly
then the report will display Balance Sheet columns for the last three months
starting from To Date
To Year and From Year
These decide the fiscal year between which the Balance Sheet report has to be
displayed. The number of columns will depend on the Periodicity.
Consolidate Columns
Checking this this will sum up all the values for each row and display a single
column.
Hide Group Amounts
The Balance Sheet report format displays the group accounts such as Current Assets
along with the non group accounts such as Cash and Debtors.
Group account balances depend on the non group account balances under them,
group account balances are not directly updated.
So clicking on Hide Group Amounts will display only the non group account
amounts making the report a bit easier to read.
Folding Rows
Rows in the Balance Sheet report which display group accounts can be clicked
to fold all the rows underneath them.