venturedrake/laravel-crm
Laravel CRM is a free, open-source, self-hosted CRM package for Laravel applications. It provides leads, contacts, accounts, deals, activities, notes, and customizable sales pipelines, plus modern Laravel authentication and Blade UI components to help you add CRM functionality quickly and easily.
crm_product_variations, crm_product_prices.product_variation_id, product_variation_id on line tables were present but unread/unwritten). For a PM this is the cheapest possible kind of feature bet: no data model migration project, no new dependencies, and a product without variations behaves exactly as before. It opens up size/colour/pack-level selling (e.g. "Small", "Gold / 18in") with per-variation SKU, barcode, price and active flag. If you have been running workarounds — duplicate parent products per size, or free-text in line descriptions — this release is the moment to consolidate and clean up the catalog.Product — Variation on all five PDF styles plus the emailed and portal documents. That directly reduces "the document doesn't match what we sold" support tickets and manual re-keying — a customer-facing quality signal, not just a schema change.Description, and the variation SKU is used as ItemCode only when a synced Xero item already has that code; otherwise the parent product's code is sent. Decision implication: if finance needs variation-level revenue, stock or margin reporting in Xero, this package does not deliver it and you should scope a separate mapping/sync project. If finance only needs the parent item with a descriptive line, you are covered.products/{product}/variations sub-resource (CRUD), variations in product responses, and product_variation_id accepted on quote/order/invoice line_items. Authorisation is inherited from the parent product, so no new permissions to administer — but note the API returns 422 for a variation belonging to another product or team, which is a useful tenant-isolation guarantee to cite in security/compliance reviews. Relevant if your roadmap includes headless storefronts or third-party integrations.comments / order_product_id were never migrated) and edits to PO lines were silently lost because the service read a request key the form never sends; creating a quote from a deal dropped the deal's products; and restored products/lunches/files recorded the restoring user in user_deleted_id instead of user_restored_id, corrupting the audit trail. These are the kind of defects that erode trust in the app faster than a missing feature — and they suggest anyone using the purchase-order module should verify existing PO data for lines that never persisted.Product::getDefaultPrice(), the API product payload and the Xero product sync all read the first matching row from crm_product_prices, a table that also holds variation prices. productPrices() and ProductPrice::scopeDefault() now exclude variation prices (and a currency comparison against a Setting model rather than its value was fixed). If any variation prices had been written before this release, reported default/product prices may have been wrong — worth a quick spot-check of headline product prices post-upgrade./customers routes and CustomerController are gone (they threw a fatal Call to undefined method ...::sortable() on open, #93), as is the unfinished Product Attributes settings page that errored with a missing table (#53), along with its model, policy, nav entries and lang keys. Fewer broken menu items and fewer "this page is broken" tickets. Directionally it also settles a product question: People and Organizations are the supported path; the Customer model/observer/policy stay only for the V1→V2 upgrade path and legacy customer_id references.config/package.php still read 2.4.7 at the time of the change. Because SystemCheckService::normalisedVersion(), the db_version marker from laravelcrm:update, the UpdateController banner and the crm.settings-seeded.<version> middleware flag all key off it, a missed bump means the "database is behind code" check compares against the previous release and seeding is not re-armed. Two consecutive releases where this needed saying is a process smell: automate the version constant in CI rather than relying on a checklist.Consider adopting or staying on this package when:
products/{product}/variations sub-resource and product_variation_id on line payloads cover integration and headless use cases without new permission design.laravelcrm:update, the UpdateController banner, settings seeding via middleware). Upgrade to 2.5.0 so the version marker, banner and seeding re-arm correctly.encrypt_db_fields; the earlier suggest-trait fix (decrypted-name comparison) and the related-contact linking fix are written to work in encrypted deployments.Evaluate carefully, or defer, when:
Description (plus a matched ItemCode when one already exists). If you need variant-level revenue, stock or margin in the ledger, budget for a separate integration project — or treat this as a reason to keep variants out of the catalog for now.laravel-crm.customers.* routes, CustomerController, StoreClientRequest/UpdateClientRequest, and the entire Product Attributes settings area (routes, controller, model, policy, nav, lang keys) are gone. Any custom code, bookmark, integration or test that referenced them will break. Audit references before upgrading; migrate to People and Organizations.PurchaseOrderService::update() now reads products using create()'s row keys (it previously read a key the form never sent), and addProduct() was fixed to read the keys the form actually submits. Custom PO forms or service overrides that assumed the old payload shape must be regression-tested.Product::productPrices() and ProductPrice::scopeDefault() now exclude variation prices. Any custom reporting, export or pricing logic that relied on the unfiltered list will change behaviour — audit before deploying.database/updates migrations add variation code, barcode, order, active and user_*_id columns, and a third adds the missing comments / order_product_id columns to crm_purchase_order_lines. Confirm your deploy path runs database/updates and consider reconciling any PO lines that failed to persist pre-upgrade.config/package.php. Two releases running, this value has been the load-bearing point of failure for upgrade banners and DB-version checks. Automate the bump or avoid depending on the built-in version-check behaviour.updatedHasPersonSuggest() / updatedHasOrganizationSuggest()) so custom updatedPersonName() handlers still fire, but deep form customisations should be regression-tested.For executives: A minor release with one substantial new capability and a set of quality fixes. Product variations let you sell size/colour/pack-level SKUs — each with its own SKU, barcode and price — and carry that choice consistently through deals, quotes, orders, invoices, purchase orders, delivery notes, PDFs, emails and the customer portal, plus a new API for programmatic management. It also fixes several defects that were costing users time or trust: contact people and organizations on the same record were never linked to each other, purchase-order lines could silently fail to save and edits to them were lost, quotes created from a deal lost the deal's products, and restored records showed the wrong user in the audit trail. Two broken legacy screens were removed (the v1 customers page and an unfinished product-attributes settings stub). No breaking changes for existing integrations, but there are three new database migrations and two removed route/UI surfaces to check before deploying. One caveat to set expectations: variations are not created as items in Xero — they appear in the line description only — so variant-level accounting reporting is a separate project.
For engineering: Upgrade to 2.5.0, a minor bump with no deprecations. Required: run the database/updates migrations — two add variation code, barcode, order, active and user_*_id columns, one adds the missing comments and order_product_id to crm_purchase_order_lines (both idempotent). config/package.php moves to 2.5.0, which drives SystemCheckService::normalisedVersion(), the db_version marker from laravelcrm:update, the UpdateController banner and the crm.settings-seeded.<version> middleware flag. New behaviour: ProductVariation model plus ProductVariationObserver (sets external_id and user_*_id); ResolvesProductVariation validates that a stored variation belongs to the line's product in the current team; pricing resolves via Product::unitPriceFor() with fallback to the product price; lines print via displayName() and displayCode() on line models; Xero behaviour lives in src/Support/XeroLineItem.php (variation name → Description, variation SKU → ItemCode only when a synced Xero item already has it). Behaviour changes to watch: Product::productPrices() returns product-level rows only, ProductPrice::scopeDefault() excludes variation prices and fixes a currency-vs-Setting-model comparison, PurchaseOrderService::update() now reads products with create()'s row keys and only matches lines belonging to the edited PO, PersonService::linkToOrganization() links person and organization bidirectionally (idempotent, preserves prior links on edit), and ProductObserver::restored(), LunchObserver::restored(), FileObserver::restored() now clear user_deleted_id and set user_restored_id. Removals: laravel-crm.customers.* routes, CustomerController, StoreClientRequest/UpdateClientRequest, customers/index view, and the whole laravel-crm.product-attributes.* surface including ProductAttribute, its policy, nav entries and lang keys — grep for references. Coverage: ProductVariationTest, ProductVariationsFormTest, ModelProductsVariationTest, DealProductVariationTest, ProductVariationConversionTest, LineItemVariationNameTest, XeroLineItemTest, ProductVariationApiTest, ProductVariationMigrationsTest, PersonOrganizationLinkTest, RestoredUserColumnsTest, CustomersRoutesRemovedTest. Post-deploy checks: update banner shows 2.5.0, laravelcrm:update writes the new db_version, settings seeding re-runs for the new flag, a PO saves and updates lines against a real database, and a deal → quote conversion carries the deal's lines and any variation.
How can I help you explore Laravel packages today?