Deployment Guide · BaseUp Labs

Installing BaseUp HR on Odoo 20

From the App Store purchase to a working payroll database — where the files go, what has to be installed first, which Python packages the manifest does not declare, and what to change before a customer touches it.

Data files
98
Version
20.0.1.0.0
Platform
Odoo 20 Enterprise
License
OPL-1
Odoo deps
25
01

What you get

One module, one Odoo application: baseup_human_resources. There are no companion modules to install and no ordering to get right.

PropertyValue
baseup_human_resourcesThe whole suite — nothing else to install
version20.0.1.0.0
licenseOPL-1 (Odoo Proprietary License v1.0)
applicationTrue — appears as a tile in Apps
depends25 Odoo modules — see §2
data files98, loaded in a fixed order at install
migrations3 scripts — see §9

Because the module is flagged as an application it shows up under the default Apps filter, so you do not have to clear the filter to find it.

What a first install does

Security groups and ACLs load first, then views and data — 98 files in a fixed sequence. Run the first install from the command line (§5) so a failure gives you the file that broke rather than a browser error.

02

Prerequisites

Odoo 20.0 Enterprise. Seven of the twenty-five dependencies are Enterprise-only modules, so the install fails outright on Community — there is no reduced mode.

Odoo module dependencies

EditionModules
Enterprise required hr_payroll · hr_payroll_account · hr_appraisal · hr_expense_extract · sign · accountant · account_accountant
Community standard base · web · mail · account · contacts · hr · hr_gamification · hr_recruitment · hr_attendance · hr_holidays · hr_holidays_attendance · hr_skills_survey · hr_expense · l10n_ph · resource · base_geolocalize · crm · spreadsheet_dashboard

All are declared in the manifest and resolved by Odoo automatically — you do not install them by hand. The split matters only for deciding whether your license can run this module at all.

Python packages

Only one package is declared

The manifest declares external_dependencies: {'python': ['requests']}, so Odoo checks for requests and reports it properly if absent. Everything else the code imports is not declared, so Odoo does not check it — a missing package produces an import traceback or a feature that fails when used, not an "unmet dependency" message.

Install Odoo 20's own requirements.txt into the server's virtualenv first. It already provides requests, the PDF library, openpyxl and XlsxWriter. Then add the two this module needs on top:

runtime packages · on top of Odoo 20's requirements
pip install geopy PyMuPDF
# optional: OCR for scanned SSS circular PDFs in Import Circular
pip install rapidocr-onnxruntime
PackageNeeded forIf missing
requestsLicense sync and license checksDeclared — Odoo reports it as an unmet dependency. Also in Odoo 20's requirements
geopyGeocoding work addresses and employee work locationsUndeclared. Import error at module load — the whole module fails, not just geofencing
pypdf or PyPDF2BIR Form 2316 PDF fillingUndeclared, but Odoo 20 itself will not start without one of them, so a working Odoo already has it
XlsxWriterPayroll Register Summary (Excel)Undeclared and imported at module load; provided by Odoo 20's requirements
PyMuPDFFlattening the issued BIR Form 2316Optional. The form is still produced, but stays an editable PDF; a warning is logged
openpyxl.xlsx files in Import CircularOptional; provided by Odoo 20's requirements. Without it only CSV imports work
rapidocr-onnxruntimeReading the scanned SSS circular PDF in Import CircularOptional. A PDF-only import reads no brackets and the preview says so

Do not add pypdf by hand. Odoo 20 pins the PDF library per Python version — PyPDF2 2.12.1 below Python 3.13, pypdf 5.4.0 from 3.13 — and the BIR report tries pypdf first, then PyPDF2, to follow whichever Odoo installed.

Do not pip install -r requirements.txt

The requirements.txt at the repository root is a development file, not a runtime list. It carries babel and genshi left over from the Aeroo reporting engine this module has migrated away from, plus test-only packages (faker, coverage, websocket-client, html2text). It deliberately omits pypdf, which Odoo 20's own requirements supply. Use Odoo's requirements.txt and the pip install lines above instead.

Server

  • Python and PostgreSQL — what Odoo 20 requires: Python 3.12 to 3.14 and PostgreSQL 16 or later. This module adds no constraint of its own.
  • Outbound HTTPS — needed for the license API, the public holiday calendar sync, the contribution-table watch, address geocoding, and cdn.jsdelivr.net, which serves Chart.js for the HR dashboard. See §6.
  • Timezone — schedules and every payroll computation localise to Asia/Manila by default.
03

Get the files

The module is distributed through the Odoo App Store. Buy it, download the archive, and unpack it somewhere outside the addons path before you go near odoo.conf.

  1. Buy the module and download the archive from your Odoo account's purchases.
  2. Unzip it somewhere outside the addons path first, and look at what came out. You should find a single directory, baseup_human_resources. The store metadata sets a limit of one installation per purchase, so check the entitlement before deploying to several databases.
  3. Note the version in baseup_human_resources/__manifest__.py and check it matches what you expect (20.0.1.0.0 at the time of writing).
Not into Odoo's own addons directory

Do not unpack the archive into odoo/addons/. That directory belongs to the Odoo distribution and your files will collide with it when Odoo itself is upgraded. Use a separate custom addons directory, as in §4.

04

Put it on the addons path

The single most common install failure. What goes in addons_path is the directory that contains baseup_human_resources — never the module directory itself, and never a level higher.

Correct layout

directory layout
/opt/odoo/custom-addons/          # <- this path goes in odoo.conf
└── baseup_human_resources/
    ├── __manifest__.py          # Odoo looks for this one level down
    ├── models/  views/  data/
    ├── security/  reports/  wizard/
    ├── controllers/  static/
    └── migrations/

Move baseup_human_resources out of the unpacked archive and into your custom addons directory, or symlink it there. The one thing that must be true is that Odoo sees a directory containing __manifest__.py exactly one level below a path entry.

odoo.conf
[options]
addons_path = /opt/odoo/odoo/addons,/opt/odoo/enterprise,/opt/odoo/custom-addons
db_host = localhost
db_user = odoo
db_password = <set this>
admin_passwd = <set this>
data_dir = /var/lib/odoo
; the dashboard, kiosk and biometric screens are asset-heavy
limit_time_cpu = 600
limit_time_real = 1200
; several payroll reports build large PDFs
limit_memory_hard = 4294967296
ownership
chown -R odoo:odoo /opt/odoo/custom-addons
find /opt/odoo/custom-addons -type d -exec chmod 755 {} \;
find /opt/odoo/custom-addons -type f -exec chmod 644 {} \;
Check for sync artefacts before deploying

File-sync tools sometimes leave duplicate copies with a space and a number in the name — migrations/20.0.1.0.0 2/, or foo 2.py. Odoo ignores migration directories whose names do not match a version, so they are inert, but they should not ship. Sweep for them: find . -name "* 2" -o -name "* 2.py".

05

Install

From the command line — preferred for a first install

You get the full traceback on stderr instead of a browser error, which matters for a module that loads this many data files.

install the module
sudo -u odoo /opt/odoo/venv/bin/python /opt/odoo/odoo-bin \
  -c /etc/odoo/odoo.conf \
  -d <database> \
  -i baseup_human_resources \
  --stop-after-init

From the interface

  1. Restart the Odoo service so the new addons path is picked up.
  2. Enable developer mode, then go to Apps and click Update Apps List. Without this the module will not appear at all.
  3. Search BaseUp and install BaseUp Human Resources. It is flagged as an application, so the default Apps filter will not hide it.
What happens on install

Ninety-eight data files load in a fixed order: security groups and ACLs first, then views and data. Along the way the module loads the Philippine statutory tables — the SSS Circular 2024-006 schedule, 24 withholding-tax brackets, 23 overtime rate rows, the ND rate table and leave types — and registers its scheduled jobs. A fresh install is ready to configure, not empty.

Odoo 20 approves attendance automatically by default. Because this module runs its own draft → validated attendance workflow, the install moves every company still on that default to Worked days require manual approval (Attendances ▸ Configuration ▸ Settings ▸ Attendance Validation). A company already set to another mode is left alone.

06

Configure

Four things to set before handing the database over. The functional setup — periods, schedules, approvers — is covered in the Operator Guide; this is the deployment layer.

License parameters

The module ships four system parameters that point at the license service. They are loaded noupdate="1", so they are written once at install and are not refreshed by later upgrades — whatever is in the database stays there until you change it.

Settings ▸ Technical ▸ System Parameters
license.sync_api_url        # license service endpoint
license.sync_api_email     # account used to authenticate
license.sync_api_password  # credential for that account
license.api_key            # shared key guarding /api/v1/license/*
Replace all four before go-live

They arrive carrying defaults. Treat them as placeholders to be replaced with values scoped to this deployment, not as working configuration — see §8, which you should act on before the database is reachable by anyone else.

Outbound network access

HostUsed byIf blocked
the license serviceSync Licenses from API, hourlyLicense records stop refreshing
public holiday serviceSync Country Holidays, dailyHolidays must be entered by hand
SSS, PhilHealth and Pag-IBIG sites, and the mirror they are watched throughSync Contribution Tables (SSS/PHIC/HDMF), monthlyTable Watch shows the sources as unreachable; rates are still loaded by hand or through Import Circular
OpenStreetMap Nominatim, through geopyGeocoding work addresses and employee work locations; address-matching check-insThe geocode buttons fail and address-matching locations cannot match
cdn.jsdelivr.netChart.js, for the HR dashboardDashboard charts do not render
Air-gapped or CDN-restricted deployments

Chart.js is declared in the backend asset bundle as an unpinned CDN URL (https://cdn.jsdelivr.net/npm/chart.js). There is no version pin and no local fallback, so the dashboard depends on a third-party host at page load and on whatever version that host currently serves. Odoo 20 ships its own Chart.js bundle (web.chartjs_lib), but this module does not use it. For a locked-down deployment, vendor the library into static/lib/ and change the asset entry to the local path.

Mail and branding

  • SMTP From — set this, or outgoing payslips and notices go out with the wrong sender.
  • System name — the app_system_name parameter replaces the product name in page titles and browser tabs.
  • Backend theme — a debranding stylesheet ships in the backend bundle and applies automatically.

Scheduled jobs

Ten jobs are created and enabled. Review them against the deployment before go-live, particularly the three that reach the network and the two biometric jobs, which will try to contact terminals that may not exist yet.

  • Auto Import Biometric Attendance · Download Attendance
  • Sync Country Holidays · Sync Licenses from API
  • Sync Contribution Tables (SSS/PHIC/HDMF) · Activate Due Circulars
  • Leave Allocation: Check Validity
  • Employee: Years in Service · Employee: Compute Age
  • HR Employee Data Expiration
07

Verify

A clean install is not the same as a working one. Six checks, in this order.

  1. Module state. Apps shows BaseUp Human Resources as Installed at 20.0.1.0.0.
  2. Menus. Employees, Attendances, Overtime, Leaves, Payroll and Salary Loans all appear for an administrator.
  3. Statutory data loaded. Payroll ▸ Configuration ▸ Premiums and Tax Tables — SSS should hold the Circular 2024-006 schedule, and Withholding Tax 24 bracket rows.
  4. Dashboard renders. Employees ▸ Dashboard. If the counts appear but the charts do not, Chart.js is being blocked — see §6.
  5. BIR report works. Open any employee, then the Print menu ▸ BIR Form 2316. A PDF must come back. Try to edit a field in it: if you can, PyMuPDF is missing and issued forms will not be flattened.
  6. Payroll computes. Generate one payroll period, one register and one payslip for a test employee with a running contract, and confirm the statutory deductions are non-zero.
Faster than reading procedures

Thirteen guided tours ship inside the module's own backend asset bundle — employee creation, payroll configuration, period generation, payslip computation, the register, DTR, leave allocation, overtime configuration, multiple attendance, biometric attendance and the chart of accounts, plus one that checks the HR dashboard renders and one that opens every menu of the module's apps. Run them from developer mode; on a fresh deployment they double as a functional smoke test.

08

Harden before go-live

Three things to settle before the database is reachable by anyone but you. None are optional on a deployment you are handing to someone else.

Give the license parameters per-deployment values

The four license.* system parameters from §6 ship with placeholder defaults so the module installs cleanly. Replace every one of them with values issued for this specific deployment, or clear them and supply the credentials another way. Because they are noupdate, an upgrade will not overwrite what you set.

Do not ship the defaults

Any user who can open Settings ▸ Technical ▸ System Parameters can read these values. Treat them like any other credential in a database you do not solely control: scope them to the deployment, and rotate them if that database changes hands.

Close the license API on customer instances

The module serves a small license API at /api/v1/license and under it, used by BaseUp's own licensing infrastructure. A customer instance never needs to answer those requests, so do not let it: block the prefix at the reverse proxy in front of Odoo. Leave the trailing slash off the location — one of the routes is /api/v1/license itself.

nginx · customer instance
location ^~ /api/v1/license {
    deny all;
    return 404;
}
Check it from outside

After deploying, confirm the block is live from a machine that is not the server: curl -i https://<host>/api/v1/license and curl -i https://<host>/api/v1/license/x should both return your 404, not an Odoo response.

Standard Odoo hardening

  • Set admin_passwd in odoo.conf and keep the database manager off the public interface — list_db = False, or block /web/database/ at the proxy.
  • Review the scheduled jobs on any instance that should not reach the network — the license sync, the holiday sync and the contribution-table sync all make outbound calls.
  • Confirm which BaseupPH Access profile each user holds. That single field drives every group and access rule the module ships, so it is the whole access-control surface.
  • Serve the instance over HTTPS only, with proxy_mode = True set in odoo.conf.
09

Upgrading

The module ships migration scripts, so upgrades run schema and data fixes automatically. Take a backup anyway — payroll data is not reconstructible.

upgrade
# 1. back up first — database and filestore
pg_dump -Fc <database> > baseup-hr-preupgrade.dump
tar czf filestore.tgz /var/lib/odoo/filestore/<database>

# 2. replace the module files, then upgrade
sudo -u odoo /opt/odoo/venv/bin/python /opt/odoo/odoo-bin \
  -c /etc/odoo/odoo.conf -d <database> \
  -u baseup_human_resources \
  --stop-after-init
  • Migration scripts present — migrations/ holds 1.0.0, 19.0.1.0.4 and 20.0.1.0.0. Odoo runs the ones between the installed version and the new one, so do not skip a version by hand-editing the manifest.
  • Coming from the 19.0 module — a 19.0 database has to go through Odoo's own major-version upgrade; swapping the module files and running -u is not enough on its own. When the module is migrated, the 20.0.1.0.0 script renames the leave-summary column that followed Odoo 20's leave-type rename (holiday_status_id to work_entry_type_id) and moves stored attendances from the old validate state to Odoo 20's validated. Without it, historic leave summaries lose their leave type.
  • License parameters survive — they are noupdate, so an upgrade will not undo the hardening in §8.
  • Watch the log for view errors — this module inherits heavily from the Odoo HR views, which is where an Odoo point-release change usually surfaces first.
10

Troubleshooting the install

SymptomCauseFix
Module not in Apps Apps list not refreshed, or the addons path points one level too high Update Apps List; confirm __manifest__.py is one level below a path entry
Unmet dependency on install Running Community, or Enterprise addons path missing Add the enterprise directory to addons_path; §2 lists the seven Enterprise modules
ImportError mentioning geopy geopy not installed; the import is at module load, so the whole module fails pip install geopy into Odoo's own virtualenv, then restart
BIR 2316 fails with "template not found" The blank form static/src/pdf/bir_form_2316_template.pdf is missing from the deployed module Redeploy the module directory complete. On Odoo 20 the report reads the template from that file
Dashboard counts but no charts Chart.js CDN unreachable Allow cdn.jsdelivr.net, or vendor the library locally — §6
Issued BIR 2316 can still be edited PyMuPDF not installed, so the form is not flattened pip install PyMuPDF, restart
Install fails in security files A partial earlier install left inconsistent groups Restore the pre-install backup and reinstall clean; do not patch ACLs by hand
Salary journal missing on a register No journal matching the expected name or code for that company Accept the guided prompt to create it, or add a sales/salary journal per company
Upgrade fails on a view An inherited Odoo HR view changed in a point release Read the failing external id in the log; the module inherits hr.view_employee_form heavily