COHAOR
Does this libary support COHAOR (Container special handling order message) , UN/EDIFACT D17B ? https://service.unece.org/trade/untdid/d17b/trmd/cohaor_c.htm
Yes, it can be done, but I haven't implemented it (I don't use that message). You can write a class similar to the existing ones which outputs the data in the Cohaor message format, take a look to the Iftmbf, or the other container related messages (for example Coparn..) If you do, please send a pull request, or write me :)
They need to be added, because they are implied in the specification. An interchange (UNB/UNZ) wraps one or more messages (UNH/UNT), in the interchange header you add inforrmation like the sender and the receiver.
What u think so far?
https://github.com/davidvandertuijn/edifact-generator/commit/62868a048190d685683c3895033a7b978c69ac1e
I have noticed the *Segment() functions in your Message Class however, i find the functionality limited and created new classses for the segments i need.
Test Script:
$sMessageReferenceNumber = 'ROW'.str_pad(1, 11, 0, STR_PAD_LEFT);
$oCohaor = (new \EDI\Generator\Cohaor($sMessageReferenceNumber))
// ->setMessageSender('IC', '', 'JOHN DOE')
// ->setMessageSenderInformation('EM', '[email protected]')
;
// Segment Group 2 : Name And Address
$oNameAndAddress = (new \EDI\Generator\Cohaor\NameAndAddress())
->setPartyFunctionCodeQualifier('ZZZ')
->setPartyIdentificationDetails('Floro Webdevelopment')
->setNameAndAddress([
'Floro Webdevelopment', // line 1 .. 5
'Westblaak 180', // line 2 .. 5
'3012KC' // line 3 .. 5
])
->setCityName('Rotterdam')
->setPostalIdentificationCode('3012KC')
->setCountryIdentifier('NL')
;
// Segment Group 2
$oCohaor->addSegmentGroup(2, [
$oNameAndAddress->compose()
]);
// Segment Group 4 : Equipment Details
$oEquipmentDetails = (new \EDI\Generator\Cohaor\EquipmentDetails())
->setEquipmentTypeCodeQualifier('AM') // Refrigerated Container
->setEquipmentIdentification('TSTU1807438')
->setEquipmentSizeAndType(4532, '', 5, '')
->setEquipmentSupplierCode('')
->setEquipmentStatusCode('')
->setFullOrEmptyIndicatorCode(6)
->setMarkingInstructionsCode('ZZZ')
;
// Segment Group 4 : Reference
$oReference = (new \EDI\Generator\Cohaor\Reference())
->setReferenceCodeQualifier('BN')
->setReferenceIdentifier('ABCDEF123456')
;
// Segment Group 4 : Date Time Period
$oDateTimePeriod = (new \EDI\Generator\Cohaor\DateTimePeriod())
->setDateOrTimeOrPeriodFunctionCodeQualifier(7) // Effective from date/time
->setDateOrTimeOrPeriodText('201811011000')
->setDateOrTimeOrPeriodFormatCode(203) // CCYYMMDDHHMM
;
// Segment Group 4 : Place Location Identification
$oPlaceLocationIdentification1 = (new \EDI\Generator\Cohaor\PlaceLocationIdentification())
->setLocationFunctionCodeQualifier('9') // Place of loading
->setLocationIdentification('BEZEE','','','')
;
$oPlaceLocationIdentification2 = (new \EDI\Generator\Cohaor\PlaceLocationIdentification())
->setLocationFunctionCodeQualifier('11') // Place of discharge
->setLocationIdentification('NLRTM','','','')
;
// Segment Group 4 : Free Text
$oFreeText1 = (new \EDI\Generator\Cohaor\FreeText())
->setTextSubjectCodeQualifier('AAA') // Good Description
->setTextLiteral(['FROZEN PULP FICTION'])
;
$oFreeText2 = (new \EDI\Generator\Cohaor\FreeText())
->setTextSubjectCodeQualifier('AEJ') // Controlled atmosphere
->setTextLiteral(['YES'])
;
$oFreeText3 = (new \EDI\Generator\Cohaor\FreeText())
->setTextSubjectCodeQualifier('AEB') // Temperature control instructions
->setTextLiteral(['DEFROSTING'])
;
// Segment Group 4 : Measurements
$oMeasurements = (new \EDI\Generator\Cohaor\Measurements())
->setMeasurementPurposeCodeQualifier('AAE') // Measurement
->setMeasurementDetails('AAO') // Humidity
->setValueRange('PER', 99 )
;
// Segment Group 4
$oCohaor->addSegmentGroup(4, [
$oEquipmentDetails->compose(),
$oReference->compose(),
$oDateTimePeriod->compose(),
$oPlaceLocationIdentification1->compose(),
$oPlaceLocationIdentification2->compose(),
$oFreeText1->compose(),
$oFreeText2->compose(),
$oFreeText3->compose(),
$oMeasurements->compose()
]);
// Segment Group 11 : Temperature
$oTemperature = (new \EDI\Generator\Cohaor\Temperature())
->setTemperatureTypeCodeQualifier('SET') // SET
->setTemperatureSetting('12.00','CEL')
;
// Segment Group 11 : Range Details
$oRangeDetails = (new \EDI\Generator\Cohaor\RangeDetails())
->setRangeTypeCodeQualifier('5') // Temperature range
->setMeasurementUnitCode('CEL')
->setRangeMinimumQuantity('10.00')
->setRangeMaximumQuantity('14.00')
;
// Segment Group 11 : Control Total
$oControlTotal = (new \EDI\Generator\Cohaor\ControlTotal())
->setControlTotalTypeCodeQualifier('16')
->setControlTotalQuantity('1')
;
// Segment Group 11
$oCohaor->addSegmentGroup(11, [
$oTemperature->compose(),
$oRangeDetails->compose(),
$oControlTotal->compose()
]);
/**
* Message Function Code.
*
* @var $iMessageFunctionCode
* @see https://service.unece.org/trade/untdid/d17b/tred/tred1225.htm
* @comment 9 : Original
*/
$iMessageFunctionCode = 9;
/**
* Document Name Code.
*
* @var $iDocumentNamecode
* @see https://service.unece.org/trade/untdid/d17b/tred/tred1001.htm
*
* @comment 292 : Inspection request
* @comment 297 : Instruction to collect
* @comment 293 : Inspection report
*/
$iDocumentNamecode = 293;
$sDocumentIdentifier = '123456'; // your unique document identifier.
$oCohaor->compose($iMessageFunctionCode, $iDocumentNamecode, $sDocumentIdentifier);
$aComposed = $oCohaor->getComposed();
echo (new \EDI\Encoder($aComposed, false))->get();
I like your approach, using a class per segment was an idea I had as well but never explored. Do you think could be worthy if (some of?) those segments could be moved in root (example: Segment\DateTimePeriod) and other classes may be refactored to use those eventually extending if a particular message differs? What you think could be the impact of creating lots of class instances (example: a full vessel generating a bayplan or a loading order of a thousand of containers)?
- Moving the segment classes to more general directory sound like a good idea, would u preffer 'segment' or 'segments' (plural) ?
- The impact of creating lots of class instances depends on the size of the data, which consumes memory. If the memory is not sufficient i would suggest sending multiple messages or batch processing.
I've no strong opinion on the plural vs singular issue, singular could be chosen for uniformity with the existing Interchange / Message (and perhaps an abstract Segment class to act as a template?) but feel free to continue as your best practice works :)
What u think so far? https://github.com/davidvandertuijn/edifact-generator/commit/e080e43c226407cbc722dbab8875a7a500c2d4fe
I moved the segments to a seperate directory.
The message content however contains trailing data elements and their leading separators:
array:6 [
0 => "LOC"
1 => "9"
2 => array:4 [
0 => "NLMSV"
1 => ""
2 => ""
3 => ""
]
3 => []
4 => []
5 => ""
]
LOC+9+NLMSV:::+++'
I've added a reduce() function in de Message class. Do you have a snippet which recursively walk an Array and remove the empty values from the end of the Array? this would be very helpfull.
I did that by checking if the value was set during the compose() run (and similarly in the segment functions), example https://github.com/php-edifact/edifact-generator/blob/master/src/Generator/Coparn.php#L293 and https://github.com/php-edifact/edifact-generator/blob/master/src/Generator/Coparn.php#L240
The architecture issue is when using the check on null values is that you must specify each value, even when they are empty else the EDI output is invalid for example:
Wrong
$oFreeText = (new \EDI\Generator\Segment\FreeText())
->setTextSubjectCodeQualifier('AEB')
->setTextLiteral(['DEFROSTING'])
->compose();
FTX+AEB+DEFROSTING'
Good
$oFreeText = (new \EDI\Generator\Segment\FreeText())
->setTextSubjectCodeQualifier('AEB')
->setFreeTextFunctionCode('') // must be empty
->setTextReference('') // must be empty
->setTextLiteral(['DEFROSTING'])
->compose();
FTX+AEB+++DEFROSTING'
How you think of this approach ?
So if one person forgets setTextReference we throw some error?
In the first approach i set all values 'empty' by default, but then i have trailing data elements and their leading separators. In the second approach i set all values NULL by default, but then is must specify empty values.
Difficult to throw an Error in the second approach, because you must check, does the value in the segment has previous required values. There is no procedure available in php-edifact to validate the $aComposed Array of a segment for example ?
I can't think of a smart way to do it without checking every value... :-/
Maybe later, is it possible you merge these changes for now ?
Done!
@davidvandertuijn anche user reports this, Could you please check (I'm not with my computer till Friday...)?
Fatal error: Declaration of EDI\Generator\Vermas::compose($msgStatus = 5, $documentCode = 749) must be compatible with EDI\Generator\Message::compose(?string $sMessageFunctionCode, ?string $sDocumentNameCode, ?string $sDocumentIdentifier): EDI\Generator\Message in /home/edifact-generator-master/src/Generator/Vermas.php on line 5
https://github.com/php-edifact/edifact-generator/issues/4