Feat: Updates to fullfilment services, shipping views, timezones, and elta labels
This commit is contained in:
@@ -116,43 +116,100 @@ class EltaFulfillmentService implements CarrierFulfillmentInterface, IssuesVouch
|
||||
|
||||
$this->client->createVoucher($params)->throwIfError();
|
||||
|
||||
$pieces = max(1, $request->packageCount);
|
||||
|
||||
// PELVGNEW doesn't return the new voucher's id, so find it in the
|
||||
// pending list by our reference. Saved even if that lookup fails —
|
||||
// the voucher exists at ELTA either way, and printing retries it.
|
||||
return Shipment::create([
|
||||
$master = Shipment::create([
|
||||
'order_id' => $order->id,
|
||||
'carrier' => 'elta',
|
||||
'source' => Shipment::SOURCE_CREATED,
|
||||
'tracking_reference' => null,
|
||||
'meta' => [
|
||||
'carrier_id' => $this->findPendingVoucherId((string) $order->reference),
|
||||
'reference' => (string) $order->reference,
|
||||
'weight' => $weight,
|
||||
'package_count' => $request->packageCount,
|
||||
'package_count' => $pieces,
|
||||
'cod_amount' => $codAmount,
|
||||
'services' => $request->serviceValues(),
|
||||
'insurance_amount' => $request->has(ExtraService::Insurance) ? $request->insuranceAmount : null,
|
||||
...($pieces > 1 ? ['piece' => 1, 'pieces' => $pieces] : []),
|
||||
],
|
||||
]);
|
||||
|
||||
// One row per extra parcel straight away, so the order shows every
|
||||
// parcel; ELTA gives them their own voucher numbers only when the
|
||||
// main voucher is issued (issue()). They belong to the main voucher:
|
||||
// no COD or services of their own.
|
||||
for ($piece = 2; $piece <= $pieces; $piece++) {
|
||||
Shipment::create([
|
||||
'order_id' => $order->id,
|
||||
'carrier' => 'elta',
|
||||
'source' => Shipment::SOURCE_CREATED,
|
||||
'tracking_reference' => null,
|
||||
'meta' => [
|
||||
'master_shipment_id' => $master->id,
|
||||
'reference' => (string) $order->reference,
|
||||
'piece' => $piece,
|
||||
'pieces' => $pieces,
|
||||
'weight' => $weight,
|
||||
'package_count' => $pieces,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
return $master;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like ELTA's own client: printing the main voucher of a multi-parcel
|
||||
* send prints every parcel (main first, then each child); printing a
|
||||
* child prints just that parcel. Either way the main voucher is issued
|
||||
* first — that's what gives the children their numbers.
|
||||
*/
|
||||
public function printLabel(Shipment $shipment): string
|
||||
{
|
||||
$this->issue($shipment);
|
||||
$master = $this->masterOf($shipment);
|
||||
|
||||
$pdf = $this->labelRenderer->render($shipment->refresh());
|
||||
$this->issue($master);
|
||||
|
||||
$shipment->update(['label_printed_at' => now()]);
|
||||
$labels = $shipment->is($master)
|
||||
? collect([$master->refresh(), ...$this->childrenOf($master)])
|
||||
: collect([$shipment->refresh()]);
|
||||
|
||||
$pdf = $this->labelRenderer->renderMany($labels);
|
||||
|
||||
Shipment::whereIn('id', $labels->pluck('id'))->update(['label_printed_at' => now()]);
|
||||
|
||||
return $pdf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Several labels in one PDF. A selected main voucher brings its
|
||||
* children along (as printLabel() does); each parcel is printed once,
|
||||
* main voucher before its children.
|
||||
*/
|
||||
public function printLabels(Collection $shipments): string
|
||||
{
|
||||
$shipments->each(fn (Shipment $shipment) => $this->issue($shipment));
|
||||
$labels = collect();
|
||||
|
||||
$pdf = $this->labelRenderer->renderMany($shipments->map->refresh());
|
||||
foreach ($shipments as $shipment) {
|
||||
$master = $this->masterOf($shipment);
|
||||
$this->issue($master);
|
||||
|
||||
Shipment::whereIn('id', $shipments->pluck('id'))->update(['label_printed_at' => now()]);
|
||||
$group = $shipment->is($master)
|
||||
? [$master->refresh(), ...$this->childrenOf($master)]
|
||||
: [$shipment->refresh()];
|
||||
|
||||
foreach ($group as $label) {
|
||||
$labels->put($label->id, $label);
|
||||
}
|
||||
}
|
||||
|
||||
$pdf = $this->labelRenderer->renderMany($labels->values());
|
||||
|
||||
Shipment::whereIn('id', $labels->keys())->update(['label_printed_at' => now()]);
|
||||
|
||||
return $pdf;
|
||||
}
|
||||
@@ -163,6 +220,16 @@ class EltaFulfillmentService implements CarrierFulfillmentInterface, IssuesVouch
|
||||
*/
|
||||
public function cancelShipment(Shipment $shipment): void
|
||||
{
|
||||
$master = $this->masterOf($shipment);
|
||||
|
||||
if (! $shipment->is($master)) {
|
||||
$piece = $shipment->meta['piece'] ?? null;
|
||||
|
||||
throw new EltaApiException(
|
||||
'This is '.($piece ? "parcel {$piece}" : 'an extra parcel').' of a multi-parcel ELTA voucher — ELTA cancels the whole voucher, so cancel the main parcel instead.',
|
||||
);
|
||||
}
|
||||
|
||||
if (filled($shipment->tracking_reference)) {
|
||||
throw new EltaApiException(
|
||||
"ELTA voucher {$shipment->tracking_reference} is already printed, and ELTA only allows cancelling vouchers that haven't been printed yet. Contact ELTA to cancel it.",
|
||||
@@ -172,6 +239,41 @@ class EltaFulfillmentService implements CarrierFulfillmentInterface, IssuesVouch
|
||||
$this->client->deleteVoucher(['pel_vg_id' => $this->pendingVoucherId($shipment)])->throwIfError();
|
||||
|
||||
$shipment->update(['cancelled_at' => now()]);
|
||||
$this->childrenOf($shipment)->each->update(['cancelled_at' => now()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The main voucher's shipment for a child parcel, or the shipment
|
||||
* itself. Children created by createShipment() point at it via
|
||||
* meta.master_shipment_id; older ones only via parent_reference.
|
||||
*/
|
||||
private function masterOf(Shipment $shipment): Shipment
|
||||
{
|
||||
if ($id = $shipment->meta['master_shipment_id'] ?? null) {
|
||||
return Shipment::find($id) ?? $shipment;
|
||||
}
|
||||
|
||||
if (filled($shipment->parent_reference)) {
|
||||
return Shipment::where('tracking_reference', $shipment->parent_reference)->first() ?? $shipment;
|
||||
}
|
||||
|
||||
return $shipment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, Shipment> the main voucher's child parcels, in piece order
|
||||
*/
|
||||
private function childrenOf(Shipment $master): Collection
|
||||
{
|
||||
return Shipment::query()
|
||||
->where('carrier', 'elta')
|
||||
->where(fn ($query) => $query
|
||||
->where('meta->master_shipment_id', $master->id)
|
||||
->when($master->tracking_reference, fn ($q) => $q->orWhere('parent_reference', $master->tracking_reference)))
|
||||
->orderBy('id')
|
||||
->get()
|
||||
->sortBy(fn (Shipment $child) => $child->meta['piece'] ?? PHP_INT_MAX)
|
||||
->values();
|
||||
}
|
||||
|
||||
public function trackShipment(Shipment $shipment): Collection
|
||||
@@ -312,16 +414,31 @@ class EltaFulfillmentService implements CarrierFulfillmentInterface, IssuesVouch
|
||||
]);
|
||||
$shipment->save();
|
||||
|
||||
foreach (array_filter(array_map('trim', (array) ($data['vg_child_no'] ?? []))) as $childVoucherNo) {
|
||||
Shipment::create([
|
||||
// The children's numbers, in ELTA's order (vg_child_no), go to the
|
||||
// pending child rows in piece order; any parcel without a row (older
|
||||
// shipments) gets one.
|
||||
$pending = $this->childrenOf($shipment)->filter(fn (Shipment $child) => blank($child->tracking_reference))->values();
|
||||
$pieces = (int) ($shipment->meta['package_count'] ?? 1);
|
||||
$childNumbers = array_values(array_filter(array_map('trim', (array) ($data['vg_child_no'] ?? []))));
|
||||
|
||||
foreach ($childNumbers as $i => $childVoucherNo) {
|
||||
$child = $pending->get($i) ?? new Shipment([
|
||||
'order_id' => $shipment->order_id,
|
||||
'carrier' => 'elta',
|
||||
'source' => Shipment::SOURCE_CREATED,
|
||||
'tracking_reference' => $childVoucherNo,
|
||||
'parent_reference' => $voucherNo,
|
||||
'label_printed_at' => now(),
|
||||
'meta' => $shipment->meta->toArray(),
|
||||
'meta' => [
|
||||
'master_shipment_id' => $shipment->id,
|
||||
'reference' => $shipment->meta['reference'] ?? null,
|
||||
'piece' => $i + 2,
|
||||
'pieces' => max($pieces, count($childNumbers) + 1),
|
||||
'weight' => $shipment->meta['weight'] ?? null,
|
||||
'package_count' => max($pieces, count($childNumbers) + 1),
|
||||
],
|
||||
]);
|
||||
|
||||
$child->tracking_reference = $childVoucherNo;
|
||||
$child->parent_reference = $voucherNo;
|
||||
$child->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user