mirror of
https://github.com/FOSSBilling/FOSSBilling.git
synced 2026-11-03 02:27:21 +09:00
* ✨ Allow custom recurring billing periods Products were limited to a fixed set of 7 hardcoded recurring billing periods (1W/1M/3M/6M/1Y/2Y/3Y), stored as fixed columns on product_payment. Admins can now configure any number of arbitrary periods per product (e.g. 45 days, 18 months, 5 years) within Box_Period's existing valid ranges, while the original 7 periods remain as pre-filled defaults and one-click quick-add presets. - Replace product_payment's fixed w/m/q/b/a/bia/tria columns with a new product_payment_period table (one row per configured period), migrated via UpdatePatcher::patch98(). - Rework the admin product pricing UI to add/remove billing periods by quantity + unit (day/week/month/year), in addition to the existing presets. - Fix Box_Period's constructor, which only ever accepted a single-digit quantity (e.g. "9Y" but not "24M" or "45D") despite its own unit bounds implying otherwise. - Fix period labels silently disappearing in the client checkout, admin order form, and admin subscription edit screens for any period outside Box_Period's 9 presets, by having the API return a precomputed title per period instead of relying on a second lookup. - Fix the Stripe adapter dropping the period quantity entirely when creating recurring prices (e.g. quarterly/biannual products were silently billed monthly), and reject periods that exceed Stripe's own interval_count caps instead of mis-billing them. Closes #4098 * Address review feedback on custom billing periods - Make patch98's period migration INSERT idempotent with ON DUPLICATE KEY UPDATE, so a rerun after a partial failure doesn't hit the (product_payment_id, code) unique constraint. - Order ProductPayment::$periods by sortOrder so the admin's edit order is preserved on reload, matching the pattern already used by ServiceDownloadable's ordered collection. - Wrap the Box_Period parsing failure in getEnabledProductPaymentPeriod() so a malformed/missing period raises FOSSBilling\InformationException instead of a raw exception, matching applyPricingToProductPayment()'s handling. - Fix the Stripe adapter reusing a wrong-cadence price: Stripe's price list filter only supports 'interval', not 'interval_count', so the existing-price match now also compares interval_count explicitly. - Stop the admin pricing form from resurrecting default periods an admin had deliberately removed - defaults are now only used when a product has no configured periods at all. - Drop the redundant product_payment_id_idx index, since the unique key on (product_payment_id, code) already covers lookups on product_payment_id via its leftmost prefix. * Fix quantity validation and i18n in the add-period JS - Use Number() + Number.isInteger() instead of parseInt() to validate the custom period quantity, so a fractional value like "5.7" is rejected instead of silently truncated to 5. - Replace the English-only '(s)' suffix hack for pluralizing the unit label with translated data-singular/data-plural attributes on each unit option, selected based on quantity.
37 lines
1.0 KiB
PHP
37 lines
1.0 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Copyright 2022-2026 FOSSBilling
|
|
* SPDX-License-Identifier: Apache-2.0.
|
|
*
|
|
* @copyright FOSSBilling (https://www.fossbilling.org)
|
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Datasets;
|
|
|
|
/**
|
|
* Valid period code test data.
|
|
*
|
|
* @return array<int, array{0: string, 1: string, 2: int, 3: float}>
|
|
*/
|
|
function periodCodes(): array
|
|
{
|
|
return [
|
|
['1D', 'D', 1, 1.0],
|
|
['7D', 'D', 7, 7.0],
|
|
['45D', 'D', 45, 45.0],
|
|
['90D', 'D', 90, 90.0], // upper bound for the day unit
|
|
['2W', 'W', 2, 15.0], // (2/4) * 30 = 15 days
|
|
['52W', 'W', 52, 390.0], // upper bound for the week unit: (52/4) * 30 = 390 days
|
|
['1M', 'M', 1, 30.0],
|
|
['3M', 'M', 3, 90.0],
|
|
['24M', 'M', 24, 720.0], // upper bound for the month unit
|
|
['1Y', 'Y', 1, 360.0], // 12 * 30 = 360 days
|
|
['2Y', 'Y', 2, 720.0], // 24 * 30 = 720 days
|
|
['5Y', 'Y', 5, 1800.0], // upper bound for the year unit
|
|
];
|
|
}
|