Skip to content

Commit fa1e554

Browse files
committed
Deduplicate BOM entry price logic into ProjectBuildHelper
The private getBomEntryUnitPrice() in ProjectBomEntriesDataTable was identical to the one in ProjectBuildHelper. Replaced it with a new public getEntryUnitPrice() on ProjectBuildHelper (returns BigDecimal, never null) and delegate to it from the DataTable. This eliminates the duplicate code and brings the DataTable lines under the existing ProjectBuildHelper test coverage. Added three tests for getEntryUnitPrice() covering the no-pricing, non-part, and part cases.
1 parent 5d669da commit fa1e554

3 files changed

Lines changed: 45 additions & 20 deletions

File tree

src/DataTables/ProjectBomEntriesDataTable.php

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@
3636
use App\Services\EntityURLGenerator;
3737
use App\Services\Formatters\AmountFormatter;
3838
use App\Services\Formatters\MoneyFormatter;
39-
use App\Services\Parts\PricedetailHelper;
40-
use Brick\Math\BigDecimal;
39+
use App\Services\ProjectSystem\ProjectBuildHelper;
4140
use Brick\Math\RoundingMode;
4241
use Doctrine\ORM\AbstractQuery;
4342
use Doctrine\ORM\Query;
@@ -55,7 +54,7 @@ public function __construct(
5554
protected TranslatorInterface $translator,
5655
protected AmountFormatter $amountFormatter,
5756
protected PartDataTableHelper $partDataTableHelper,
58-
protected PricedetailHelper $pricedetailHelper,
57+
protected ProjectBuildHelper $projectBuildHelper,
5958
protected MoneyFormatter $moneyFormatter,
6059
) {
6160
}
@@ -212,15 +211,15 @@ public function configure(DataTable $dataTable, array $options): void
212211
'label' => 'project.bom.price',
213212
'visible' => false,
214213
'render' => function ($value, ProjectBOMEntry $context) {
215-
$price = $this->getBomEntryUnitPrice($context);
214+
$price = $this->projectBuildHelper->getEntryUnitPrice($context);
216215
return $this->moneyFormatter->format($price->toScale(2, RoundingMode::UP)->toFloat(), null, 2, true);
217216
},
218217
])
219218
->add('ext_price', TextColumn::class, [
220219
'label' => 'project.bom.ext_price',
221220
'visible' => false,
222221
'render' => function ($value, ProjectBOMEntry $context) {
223-
$price = $this->getBomEntryUnitPrice($context);
222+
$price = $this->projectBuildHelper->getEntryUnitPrice($context);
224223
return $this->moneyFormatter->format(
225224
$price->multipliedBy($context->getQuantity())->toScale(2, RoundingMode::UP)->toFloat(),
226225
null,
@@ -258,21 +257,6 @@ function (QueryBuilder $builder) use ($options): void {
258257
]);
259258
}
260259

261-
private function getBomEntryUnitPrice(ProjectBOMEntry $entry): BigDecimal
262-
{
263-
if ($entry->getPart() instanceof Part) {
264-
$amount = $entry->getQuantity();
265-
// If the BOM quantity is below the minimum order amount, use the minimum order amount
266-
// for the price lookup — otherwise calculateAvgPrice returns null (no price tier matches).
267-
$minOrderAmount = $this->pricedetailHelper->getMinOrderAmount($entry->getPart());
268-
if ($minOrderAmount !== null) {
269-
$amount = max($amount, $minOrderAmount);
270-
}
271-
return $this->pricedetailHelper->calculateAvgPrice($entry->getPart(), $amount) ?? BigDecimal::zero();
272-
}
273-
return $entry->getPrice() ?? BigDecimal::zero();
274-
}
275-
276260
private function getFilterQuery(QueryBuilder $builder, array $options): void
277261
{
278262
$builder

src/Services/ProjectSystem/ProjectBuildHelper.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,16 @@ public function roundedUnitBuildPrice(Project $project, int $number_of_builds =
227227
?->toScale(2, RoundingMode::UP);
228228
}
229229

230+
/**
231+
* Returns the effective unit price for a single piece of the given BOM entry,
232+
* taking bulk pricing and minimum order amounts into account for N builds.
233+
* Returns BigDecimal::zero() when no pricing data is available.
234+
*/
235+
public function getEntryUnitPrice(ProjectBOMEntry $entry, int $number_of_builds = 1, ?Currency $currency = null): BigDecimal
236+
{
237+
return $this->getBomEntryUnitPrice($entry, $number_of_builds, $currency) ?? BigDecimal::zero();
238+
}
239+
230240
/**
231241
* Returns the effective unit price for a single piece of the given BOM entry,
232242
* taking bulk pricing into account for N builds.

tests/Services/ProjectSystem/ProjectBuildHelperTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,37 @@ public function testCalculateTotalBuildPriceMixedEntries(): void
264264
$this->assertTrue(BigDecimal::of('11.00')->isEqualTo($result));
265265
}
266266

267+
public function testGetEntryUnitPriceReturnsZeroForNoPricingData(): void
268+
{
269+
$entry = new ProjectBOMEntry();
270+
$entry->setPart(new Part()); // part with no orderdetails
271+
$entry->setQuantity(5);
272+
273+
$result = $this->service->getEntryUnitPrice($entry);
274+
$this->assertTrue(BigDecimal::zero()->isEqualTo($result));
275+
}
276+
277+
public function testGetEntryUnitPriceNonPartEntry(): void
278+
{
279+
$entry = new ProjectBOMEntry();
280+
$entry->setName('Wire');
281+
$entry->setQuantity(2);
282+
$entry->setPrice(BigDecimal::of('1.25'));
283+
284+
$result = $this->service->getEntryUnitPrice($entry);
285+
$this->assertTrue(BigDecimal::of('1.25')->isEqualTo($result));
286+
}
287+
288+
public function testGetEntryUnitPriceWithPart(): void
289+
{
290+
$entry = new ProjectBOMEntry();
291+
$entry->setPart($this->makePartWithPrice(2.00));
292+
$entry->setQuantity(3);
293+
294+
$result = $this->service->getEntryUnitPrice($entry);
295+
$this->assertTrue(BigDecimal::of('2.00')->isEqualTo($result));
296+
}
297+
267298
public function testCalculateTotalBuildPriceRespectsMinOrderAmount(): void
268299
{
269300
$project = new Project();

0 commit comments

Comments
 (0)