It's used in almost all systems, we should have it as a php 5.3 package. The AasTariff
-class
from Shop is already quite good:
- it should return a class instance though (HsCode-instance, immutable and lazily constructed)
- add getDefault()-method.
- Change fromMaterial() to return 'null' instead
The Asendia Client needs to learn a few new tricks. As we have learned: We can bundle up to 100 requests at a time.
We should definitely make use of it, as it will reduce our request times substantially.
- AsendiaClient will not offer direct methods for making requests anymore.
- Instead all requests will be instantiated and either passed into
AsendiaClient::call()
as an array or via->call()
on the object itself. - Afterwards calling
getResponse()
on any request will return the associated response for that request.
We will need to implement at least the following commands:
- ParcelDataRequest/Response
- CustomsDataOutboundRequest/Response
- DispatchListRequest/Response
- DeleteParcelRequest/Response
We'll have lots of routes, each of them represented by a class. Every route that behaves differently will be its own class.
We should do this for remaining old routes as well as for the new ones.
For instance there would be 6 different classes for routes to Belgium and other countries with special treatment
- Belgium LV THIN
- Belgium LV THICK
- Belgium HV THIN
- Belgium HV THICK
- Belgium HVT THIN
- Belgium HVT THICK
And a total of 6 classes for other EU countries:
- LV_EU_OTHER_THIN
- LV_EU_OTHER_THICK
- HV_EU_OTHER_THIN
- HV_EU_OTHER_THICK
- HVT_EU_OTHER_THIN
- HVT_EU_OTHER_THICK
And a total of 2 classes for the rest of the world:
- LV_OTHER_THIN
- LV_OTHER_THICK
etc.
Every class will be pretty much static and won't offer that much logic. They will all implement the same interface though to be compatible at all times. They will react to different kind of hooks throughout the process.
Each class can be tested quickly.
When a delivery note has no route selected yet, it (or a part of it) will be passed into a
SHIPPING_ROUTE_RESOLVER
-service implementing RouteResolverInterface
.
interface RouteResolverInterface {
resolve(string $countryCode, float $euroValue, string thickness = THICKNESS_THIN): RouteInterface;
get(string $routeName): ?RouteInterface;
}
It will ALWAYS return a class implementing RouteInterface, which can be uniquely identitified via getName(). This is can be tested easily as well.
This will also allow us to add special rules when switching to the new system. For example we could resolve the new route only for up to 50 parcels every day for each route. Reducing the negative effects in case of failure.
/*
* Note: Instances of this class are pretty much immutable!
*/
interface RouteInterface {
// Essentials
getName(): string; // can be derived from ::class
isCompatible(string $countryCode, float $euroValue, string thickness = THICKNESS_THIN): bool;
// TBD
requiresCN22(): bool;
requiresPrintedInvoice(): bool;
requiresWeight(): bool;
requiresAsendiaSync(): bool;
getPackageName(): name;
getSortingIcons(): string[];
getSortingBin(): string;
getRouteData(): array; // ???
// ...
}
The resulting route would then be assigned to the delivery note.
The Route object will required again and again throughout the shipping process. So whenever required the original
route class can be retrieved via its originally retrieved identifier from getName()
, by calling get()
on the
shipping route resolver service.
The route should contain all required flags for the systems to work and may also be used for stateless logic. Routes not using certain features should be generally compatible with them as defined via the interface.
Example: Not all deliveries require a CN22 label. So they should return false in requiresCn22Label()
to skip the
process, but they should not fail.
For the new SwissPost route we need to print a lable containing their barcode, which we'll receive via the AsendiaClient. The request however is too slow, forcing us to take a different approach:
The DeliveryNotes table will be expanded to include the sync state with the external service.
When transfering DeliveryNotes from the CollectionCenter to the QA, all routes requiring asendiaSyncOnce will issue a ParcelDataRequest. The response will contain a unique barcode, which will be saved. It will be used in later requests with the Service and also rendered onto the label in the QA.
The initial data being sent is a "best guess"-estimate of the parcel data and the exact data sent, will be saved in an additional table.
If the data changes during QA (for instance the thickness, a partially/fully missing item, etc.), it will be marked as unsynced/dirty again. Same is true for a complete cancellation.
A cronjob will regularly pick up all unsynced parcels and update/delete these via the AsendiaClient.
Also: For some reason i don't understand (yet) we have to send the customs data for each parcel individually AFTER creating it and BEFORE finalizing the package.
This is a separate request, but could possibly be handled by the same cronjob (maybe as a 2nd task)
Apart from our "old" LV packages, we may have different processed/checks when closing packages.
Grouping of items is generally handled via the Routes as they can specify a package group and sorting symbols.
We'll have A LOT more groups to sort though for the new route:
- 4 final package types (A,B,C,D)
Every pile will be separated within the Package and has its own weight.
<22eu, LV_EU_, LV_EU_OTHER, LV_USA, LV_CANADA, LV_OTHER
- 28 piles: 14 special EU countries * 2 formats (thick & thin)
- 2 piles: 14 other EU countries * 2 formats (thick & thin)
- 6 piles: USA/Canada/Other * 2 formats
- 2 piles: Switzerland * 2 formats
22eu-200eu, HV_EU
- 26 piles: 13 special EU countries * 2 formats (thick & thin)
- 2 piles: 15 other EU countries * 2 formats (thick & thin)
200eu, HVT_EU_ HVT_EU_OTHER
- 26 piles: 13 special EU countries * 2 formats (thick & thin)
- 2 piles: 15 other EU countries * 2 formats (thick & thin)
This totals in 94 piles if i'm not mistaken. OUCH!
Package Type A is pretty much what we have already. B, C and D will be part of the new route.
Finishing a package is generally IMPOSSIBLE if there are related packages marked as unsynced. When closing a package we need to be absolutely sure, that we only mark synced items as dispatched, as it is impossible to update them afterwards in case of an error!
The DispatchListRequest offers many different interactions, but will only accept up to 100 IDs at the same time. I hope this is wrong, but it might be intentional...
We'll have to find out how to handle this properly as the alternatives are foggy at best. (Like dispatch all within a time range..., which may include unsynced items...)
The new route will require labels with a very specific format:
- 148mm wide
- the top 40mm are equally split into Sender (left) and Postage data (right).
- the lower area is for the Recipient's data and has a margin of at least 15mm (right, bottom and left, bottom)
We can print minor remarks and icons into the lower area's bottom margin:
- right bottom will have our sorting codes
- left bottom will have our Order number (and maybe a tiny barcode)
The Stickers we currently use are 100mm wide. To keep the old route working we intend to reuse these for the foreseeable future. The solution is to print the parcel label on two stickers (right-aligned). They will be attached next to each other by the worker.
This will leave an area of roughly 5cm unused on the left side of the first label. We will use this area to render our CN22 label if required.
In the future we may change the label format, but for now this should be ok.
For the new route we need to add a printed invoice for each parcel. This is different from the printed delivery note we add for some routes as it will contain more details informations and will have a slightly different layout. They will also show toll and tax information, which again requires the route to be determined as countries have different regulations for this.
These will added in the CollectionCenter after the route has been determined.