DEV3 Palletra – B2B Wholesale Ordering Laravel Script with Price Lists, Credit Terms & Quotes (Flutter Apps)

A self-hosted B2B wholesale platform on Laravel 12 and Filament 4 with two Flutter apps: per-company price lists and tiers, case MOQs, credit limits and holds, approval-level quotes, invoices, receipts and rep offline orders. Web installer and demo data included.

by DEV3 TechnologiesVerified Author(0) 0 sales 0 viewsUpdated 04 Nov 2026

Video tour. Pick a section below to browse 112 screenshots in 27 sections.

Palletra is a B2B wholesale ordering platform that you host yourself, built by DEV3 Technologies on Laravel 12 and Filament 4, with two Flutter apps: one for buying companies and one for the supplier's sales reps. One supplier sells to approved trade accounts. Each account sees its own prices, orders by the case, buys on credit terms and negotiates quotes with a rep. Staff work in an admin panel and buyers also get a web portal. The demo supplier is a fictional distributor, Northgantry Supply Co., serving catering, janitorial and packaging customers around the port of Halvard.

No live demo: 112 grouped screenshots and two video tours show the marketing site, the buyer portal, the admin panel and both apps. Install it with the bundled demo data to try every role on your own server.

Prices that depend on who is asking

  • Price lists per company, with quantity tiers, validity dates and a parent chain up to five levels deep. The first list that has a row for the item decides, and it is never "lowest wins".
  • Contract prices for one company and item, with their own minimum quantity, validity and optional MOQ override. A contract beats any list.
  • Mix and match lets quantities across the variants of one product count together toward a tier.
  • A price simulator in the admin shows the resolved price and its source for any company, item and quantity. The same test vectors run in the PHP tests and in both Flutter apps.
  • Guests see the catalogue without prices. Company sign-up is an application that staff approve with a price list, terms, credit limit and rep.

Ordering by the case

  • Every variant has a minimum order quantity and a step. A quantity that is not a valid case multiple snaps to the nearest valid one, with a suggestion from the server.
  • Quick order takes pasted SKUs and quantities, saved lists hold standing orders, and the cart shows how many more cases unlock the next price break and how far the order is from the company's minimum order value.
  • Checkout takes a PO number, address and terms and shows the credit impact before you place the order. A company can set a spending limit per user, so a larger order waits for a company approver, and expires if nobody decides.

Credit terms and holds

  • Each company has a credit limit and payment terms: prepaid, net days, or end of month, optionally with an early-payment discount such as 2/10 net 30. Terms are copied onto the order, so changing them affects only new orders.
  • Exposure is open receivables plus unbilled orders. An order that exceeds the available credit, or comes from a company with an overdue invoice past the allowed days, goes on credit hold without reserving stock. Credit controllers release it within the limit or override with a reason that is audited, and an hourly job releases held orders when they fit again.
  • Every check is made under a database lock on the company, and the race checks run against MySQL.

Quotes with approval levels

  • Reps build quotes in the app with a live read-out of discount, margin and the approval level they need: rep, manager or director, set by rules for maximum discount, minimum margin and maximum value. Non-USD totals are converted for the value test.
  • Approval is sequential, an approver cannot be the quote's own rep, and any edit after submission creates a new version and clears earlier approvals. Buyers never see cost, margin or the price source.
  • Buyers compare versions, comment, decline or accept. Accepting creates one order with the quoted prices locked, and a second accept returns the same order. Quotes expire by date.

Fulfilment, invoices and receivables

  • Stock is allocated by warehouse priority, backorders can be shipped later or cancelled, and each shipment gets its own invoice. Tax is split across invoices so their totals equal the order exactly, and freight goes on the first invoice.
  • Receipts are allocated oldest first or to chosen invoices, with the remainder kept on account. The early-payment discount is issued as a credit note when payment arrives in the window.
  • Returns are approved, received, restocked if flagged and credited at the original price with proportional tax. Return credits can never exceed what was invoiced less what was already credited, and after an early-payment discount they are limited to what the buyer actually paid for those lines.
  • Statements with ageing buckets, PDF invoices and statements, a dunning log with one reminder per level per invoice, and a daily job that checks the ledger adds up.

For sales reps

  • The rep app has a today view, an account list with credit bars, a company 360 page, a route map, check-in with a flag when the rep is more than 500 metres away, a catalogue with margin chips, orders on behalf of a company, the quote builder, an approvals inbox for managers, commissions and targets.
  • Offline orders are queued with a client reference and re-priced by the server on sync. If prices moved, nothing is created until the rep confirms a diff. Resending the same reference never creates a second order.
  • Commission is booked when an invoice is paid and reversed in proportion by later credit notes.

Admin panel

  • Roles for admin, sales manager, sales director, sales rep, pricing, credit controller and warehouse on one Filament panel: dashboard with revenue against target, receivables ageing, quote funnel and a 3D warehouse view, applications, companies and credit, price lists and tier grid with CSV import and export, contract prices, quotes and approvals, orders, backorders, shipments, returns, invoices, receipts, credit notes, statements, dunning, commissions, visits map and reports on revenue, margin, discount leakage and quote win rate.
  • The core adds settings, roles, an audit log, devices, wallets, tax rates and exchange rates.

The Flutter apps

  • Buyer app: onboarding, catalogue with tier tables, case stepper, cart, checkout, orders and tracking, internal approvals by swipe, quotes with version compare and accept, invoices and a pay sheet, statements, credit and ageing, tablet layout and Arabic. It starts in demo mode with its own data and switches to your server with two build flags.
  • Rep app: the features above, in light and dark, phone and tablet, English and Arabic.

What is included

  • The Laravel application with Composer packages and compiled assets inside (no Node.js or Composer on the server), both Flutter projects in apps/, the shared DEV3 packages, a web installer with demo or empty data, and documentation.
  • English with an Arabic translation, light and dark themes.

Good to know

  • Live payment gateways, SMS and push were not tested with real accounts. Card payments run on a simulated gateway by default, and push and SMS use simulated or log drivers. The rule that money lands only when the gateway confirms was tested against the simulated gateway. Run your own sandbox payments before taking real ones.
  • The apps were not tested on physical devices. Both build as debug Android packages and run on the iOS simulator. The buyer app's real-server path (sign-in, invoice list, invoice detail, paying by invoice id, the invoice PDF) was exercised over HTTP against a seeded local backend. The rest of both apps was tested against a built-in mock and API responses captured from the backend, so plan your own end-to-end pass on a phone.
  • No re-invoice flow after voiding an invoice. Voiding reverses the ledger and counters, and only works on an invoice with no payments applied. Creating a new invoice for the same shipment is not built.
  • Early-payment discount and returns. A return after the discount was taken is credited only for what the buyer paid for those lines, with tax credited in full. If goods are returned before the invoice is paid, the discount is still worked out on the original invoice subtotal.
  • Freight is not credited by a return. Return credit notes cover goods and tax only.
  • One supplier, one base currency. Palletra is a single-seller platform. Each company has one currency, and dashboards convert EUR to USD with the exchange rates you maintain.
  • Tax is table-driven. Rates are looked up by country, region and tax class, and tax-exempt documents are checked for approval and expiry. It is not a tax engine and was not checked against any real jurisdiction.
  • No carrier integration. Carrier, tracking number and route points on a shipment are entered by staff or the demo data.
  • Databases: MySQL 8 and SQLite were tested. MariaDB and PostgreSQL were not.
  • Languages: English, with an Arabic translation and right-to-left layout. The Arabic file is not guaranteed to cover every string.
  • Name: Palletra is a working name checked by a web search only, not a trademark clearance. Check it before you launch under it.
  • No hosted demo. All companies, people and products in the screenshots are fictional.

Quality checks

Palletra 1.0.0 passed the seven release gates of the DEV3 Laravel suite: static analysis (Larastan level 6, 0 errors), 171 Pest tests with 764 assertions, a front-end size budget, migrations and seeding on MySQL 8 and SQLite, a headless-browser pass over 79 pages and 14 happy paths with parallel-request race checks on MySQL, an installer test from the zip, and a secret scan. The buyer app passed analysis and 150 tests, and the rep app 85 tests, plus debug Android and iOS simulator builds.

How it's built

Architecture, stack, key decisions and the quality gates DEV3 Palletra passed before release.

The engineering behind Palletra: a pure price resolver shared with the Flutter apps, credit checks under a company row lock, sequential quote approval with versions, split invoices that add up exactly, and an AR ledger with a daily reconciliation.

The problem it solves

Wholesale looks like retail with bigger quantities until the rules arrive. The same item has a different price for each customer, and which price wins must be predictable. Quantities must be whole cases. Customers buy on credit, so two orders that each fit the limit can together exceed it unless the check is serialised. Reps discount in quotes, and large discounts need a manager or director, with every edit invalidating earlier approvals. Orders ship in pieces, each shipment is invoiced, and the tax on the pieces must add up to the order. Payments arrive late, partly, or with an early-payment discount, and returns must credit only what a buyer actually paid. Reps also take orders offline, and prices may move before they sync. Palletra had to get these rules right on the server first, then expose them through one API for the portal and both apps.

Architecture and approach

Palletra is a Laravel 12 application with two Filament 4 panels: /admin for staff roles and /portal for buying companies. The marketing site is Blade with a lazy-loaded three.js scene. Both Flutter apps use the same /api/v1 routes, and a token made for one app is refused on the other app's routes. Business rules live in services under app/Services, so the portal, the API and the admin share them.

Rules in services

  • Prices. PriceResolver is a pure service: a quote line, then a contract price, then the first company list in the parent chain that has a row for the item, then the base price. A shared vector file runs in Pest and in both apps, so the apps can price offline with the same answer.
  • Cases. MoqValidator checks the minimum and the step and suggests the smallest valid quantity.
  • Credit. CreditService locks the company row, then sums open receivables and unbilled orders. Held orders reserve no stock, and an hourly job releases them oldest first when they fit.
  • Quotes. QuoteApprovalService picks the smallest level whose rules pass, runs approvals in sequence and snapshots a version on every edit. Accept, comment, decline and withdraw lock the quote row, and accept also locks the company first so the credit check sees committed orders.
  • Invoices. FulfilmentService and InvoiceService make one invoice per shipment. The last shipment of a line takes the tax remainder, so invoice totals equal the order total exactly.
  • Receivables. ArLedger writes debits and credits to a company wallet with idempotent keys, and ReceiptAllocator applies receipts, discounts and credit notes. A daily job proves that the ledger balance equals open invoices less unallocated receipts and credit notes.
  • Returns. RmaService credits at the original unit price with proportional tax. When an early-payment discount was taken, the discount is spread across invoice lines by cumulative rounding, so repeated partial returns never total more than the buyer paid.

Money and safety

  • Card money applies only on a payment-succeeded event, never on the client's word. Money is stored as integer minor units with half-up rounding helpers.
  • Rep orders carry a client reference, so a retry returns the same order, and the server re-prices them on sync and refuses to create the order until the rep confirms any price difference.

Scheduler

One cron line runs palletra:jobs for credit review, order expiry, invoice ageing and dunning, quote expiry, document expiry warnings and the ledger check. Queues default to sync, so no worker is required to start.

The apps

Both Flutter apps include a mock of the API and start in demo mode. Two build flags point them at a real server. The buyer app takes invoice ids from the API response and never derives them from invoice numbers.

Quality gates and results

Palletra 1.0.0 passed the seven release gates of the DEV3 Laravel suite:

  • Static analysis: composer validate, Pint clean and Larastan level 6 with 0 errors.
  • Tests: 171 Pest tests with 764 assertions, including price vectors, credit exposure, quote levels, fulfilment property tests, ledger invariants after random sequences, and returns after an early-payment discount (full, partial, none and repeated partial).
  • Races: parallel requests against MySQL for orders against one credit limit, quote accepts, the last unit of stock and repeated client references.
  • Database: demo and empty seeders on MySQL 8 and SQLite.
  • Browser pass: 79 pages and 14 happy paths in a headless browser, and every list tab loaded as the role that sees it.
  • Installer and secrets: the web installer ran from the zip and the secret scan was clean.

The buyer app passed analysis and 150 tests, the rep app 85 tests, and both build for debug Android and the iOS simulator.

Limits: live payment, push and SMS were not run with real accounts, the apps were not run on physical devices, there is no re-invoice flow after voiding an invoice, and MariaDB and PostgreSQL were not tested.

Tech stack

Area Technologies
Backend Laravel 12, Filament 4 panels, REST API under /api/v1, Scheduler jobs, Row-lock transactions
Wholesale engine Price resolver, MOQ validator, Credit service, Quote approval service, Receipt allocator, AR ledger
Frontend Blade, Tailwind CSS 4, Vite 7, three.js warehouse hero, Warehouse Twin view
Mobile Flutter buyer app, Flutter rep app, Riverpod and go_router, Built-in API mock, Captured API fixtures
Data MySQL 8, SQLite, Integer money, Demo and empty seeders
Integrations Card gateway interface, Simulated gateway default, Push and SMS drivers

Inside the build

Price-break hero: Cartons stack into volume tiers on a night warehouse floor while a scan beam prices each level. Price-break hero — Cartons stack into volume tiers on a night warehouse floor while a scan beam prices each level.

Floor: Request access and get your own price list, terms and limit. Floor — Request access and get your own price list, terms and limit.

Guides & articles

Practical guides and engineering write-ups for DEV3 Palletra.

B2B wholesale ordering software: 7 things to check before you pick one

Last updated
B2B wholesale ordering software: 7 things to check before you pick one: DEV3 Palletra

Wholesale demos all show the same thing: a catalogue, a cart, a login. The differences appear in the first month of real trade, when two customers see the same item at different prices and one of them is wrong, when a buyer orders 7 of something sold in cases of 12, when two orders that each fit a credit limit together do not, when a rep gives away more margin than a manager allowed, or when a return credits more than the customer paid. This guide lists seven things to test before choosing self-hosted B2B ordering software, and how Palletra, built on Laravel 12 and Filament 4 with two Flutter apps, handles each one.

The Palletra buyer portal dashboard: credit gauge, receivables ageing, reorder suggestions and open orders

1. Does every customer get the right price, and can you explain it?

Ask what wins when a customer has a contract, a list and a base price at once, and whether the answer is "lowest" or a defined order. Prices that depend on quantity tiers need the same answer on the web, in the app and in a quote.

Palletra resolves a price in a fixed order: quote line, contract price, the first company list in a parent chain that has a row for the item, then the base price. A price simulator in the admin shows the winner and its source, and the same test vectors run in the PHP tests and in both apps.

The Palletra tier table: the row for the entered quantity lights up as you type

2. Are quantities whole cases?

Selling by the case means a minimum and a step. Check what happens with an invalid quantity: an error, or a helpful correction.

In Palletra each variant has a minimum and a step, a contract can override the minimum, and the server suggests the smallest valid quantity. The apps snap typed values to it. Reps cannot bypass the rule on normal orders, and only a manager-level quote can waive it.

3. Is credit checked in a way that two orders cannot beat?

A credit limit read by two requests at once is not a limit. Look for locking, and for what happens to an order that is held.

Palletra locks the company row, then adds open receivables and unbilled orders. An order over the available credit, or from a company with an invoice overdue past the allowed days, goes on hold without reserving stock. Credit controllers release it within the limit or override with a reason that is audited. Parallel-request tests against MySQL check that of two orders that fit alone but not together, one is confirmed and one is held.

The Palletra credit hold release dialog: release within the limit, or override with a reason

4. Do quotes need the right person to approve them?

If reps can send any discount, margin goes missing quietly. Look for levels, order and versioning.

Palletra sets the approval level from rules for maximum discount, minimum margin and maximum value, and runs manager then director when both are needed. An approver cannot be the quote's own rep, and any edit after submission creates a new version and clears earlier approvals. Buyers see unit prices and totals, never cost or margin, and accepting creates one order at the quoted prices.

The Palletra quote builder: the card turns amber when a discount needs manager approval

5. Do invoices, payments and credit notes add up?

Partial shipments, partial payments and early-payment discounts break simple accounting. Ask whether there is a ledger and a way to prove it balances.

Palletra issues one invoice per shipment, splits tax so invoices equal the order exactly, allocates receipts oldest first or to chosen invoices, and issues a credit note when the early-payment discount applies. A daily job checks that the ledger equals open invoices less unallocated receipts and credit notes. Statements show ageing buckets.

The Palletra record-receipt screen: allocate oldest first or to chosen invoices

6. Do returns credit only what was paid?

A return after an early-payment discount is where many systems over-credit. Check partial returns, repeated returns and tax.

In Palletra a return is credited at the original unit price with proportional tax, and never above what was invoiced less what was already credited. When a discount was taken, the credit for those lines is reduced by the same share, so the total of repeated partial returns equals the amount paid and not more. Void works only on invoices with no payments.

7. Does the rep app work with a poor signal?

Reps visit warehouses and kitchens with patchy coverage. Ask what happens to an order taken offline when prices change before it syncs.

The Palletra rep app queues the order with a client reference. On sync the server prices it again, and if anything moved, nothing is created until the rep confirms a diff sheet. Resending the same reference never creates a second order. The app also has a route map, check-in with a distance flag, a catalogue with margin chips, commissions and targets.

The Palletra rep app offline sync: old and new prices side by side before the order is sent

Apps and admin

Palletra includes a buyer app and a rep app in Flutter. Both start in demo mode with their own data, and two build flags point them at your server. The buyer app passed analysis and 150 tests, and the rep app 85 tests. The real-server path of the buyer app (sign-in, invoices, paying, the PDF) was run against a seeded local backend, but neither app was tested on a physical device.

See it in action

The video tour goes from the 3D marketing site through applying for an account, tier prices, the cart, a credit hold and its release, shipping, invoicing and paying, to a quote and the admin warehouse view.

Video tour of Palletra

What else to compare

  • Roles: admin, sales manager, sales director, sales rep, pricing, credit controller and warehouse, plus company admin, approver, purchaser and viewer roles for buyers.
  • Reports: revenue against targets, margin, discount leakage and quote win rate.
  • Languages: English, plus an Arabic translation with right-to-left layout.
  • What is not built: a re-invoice flow after voiding an invoice, carrier integrations and multi-supplier marketplaces.
  • What was not verified: live payment gateways, SMS and push with real accounts, and the apps on physical devices.
  • Quality: Palletra 1.0.0 shipped with 171 Pest tests (764 assertions), Larastan level 6 with no errors, a headless-browser pass over 79 pages and 14 happy paths and an installer test from the zip.

Instead of a live demo, the listing has 112 grouped screenshots and two video tours, and the "How it's built" section explains the architecture.

Frequently asked questions

The complete source code (version 1.0.0) as a download from your account, with the setup and usage guides shown on this page. You can modify the code to suit your product.

Buying guides: Laravel scripts with Filament panels and a web installer · Next.js or Laravel for a SaaS script?

More from this author

View all

Related in Web Scripts & Software

View all