Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.eclipse.dirigible.components.intent.model.EntityIntent;
import org.eclipse.dirigible.components.intent.model.LabelExpression;
import org.eclipse.dirigible.components.intent.model.FieldIntent;
import org.eclipse.dirigible.components.intent.model.NumberIntent;
import org.eclipse.dirigible.components.intent.model.IntentModel;
import org.eclipse.dirigible.components.intent.model.RelationIntent;
import org.eclipse.dirigible.components.intent.model.RollupIntent;
Expand Down Expand Up @@ -924,6 +925,32 @@ private static Map<String, Object> propertyMap(String entityName, FieldIntent fi
if ("uuid".equalsIgnoreCase(field.getType())) {
p.put("generatedUuid", "true");
}
// First-class document numbering (intent `number: {}`): the platform maintains a per-series
// counter and stamps the formatted number. stampOn:create stamps the real number on insert
// (the generated repository calls sdk.numbering.DocumentNumbers); stampOn:issue holds a UUID
// placeholder on create (reusing the uuid auto-fill above) until the generated stamp step runs.
if (field.getNumber() != null) {
NumberIntent number = field.getNumber();
List<String> numberScope = new ArrayList<>();
if (number.getScope() != null) {
for (String scopeName : number.getScope()) {
// Scope names index the counter AND read the entity's field on create; PascalCase them
// to match the generated entity property (year stays the literal token).
numberScope.add("year".equalsIgnoreCase(scopeName) ? "year" : IntentNaming.pascalCase(scopeName));
}
}
p.put("numberSeries", number.getSeries() == null ? entityName : number.getSeries());
p.put("numberFormat", number.getFormat() == null ? "" : number.getFormat());
p.put("numberScope", numberScope);
p.put("isReadOnlyProperty", "true");
if ("issue".equalsIgnoreCase(number.getStampOn())) {
p.put("numberStampOn", "issue");
p.put("generatedUuid", "true"); // UUID placeholder on create; stamped at the issue step
} else {
p.put("numberStampOn", "create");
p.put("numberStampOnCreate", "true"); // real number allocated + formatted on insert
}
}
if (field.isSensitive()) {
// Hidden from the personal (my) surface: absent from its pages and stripped from the
// personal REST controller's responses. The power surface ignores this attribute.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,39 @@ void customerEmitsCrossModelProjectionsAndForeignKeys() {
assertEquals("master-data", customer.get("perspectiveNavId"));
}

@Test
void numberFieldEmitsStampMarkers() {
String yaml =
"""
name: billing
entities:
- name: SalesInvoice
fields:
- { name: id, type: integer, primaryKey: true, generated: true }
- { name: number, type: string, number: { series: SalesInvoice, format: "SI-{seq:07}", scope: [year], stampOn: issue } }
- name: Proforma
fields:
- { name: id, type: integer, primaryKey: true, generated: true }
- { name: number, type: string, number: { series: Proforma, format: "PF-{seq:05}", stampOn: create } }
""";
Map<String, Object> model = EdmIntentGenerator.buildModelJsonForTest(IntentParser.parse(yaml), "billing");
List<Map<String, Object>> entities = entities(model);

// stampOn: issue -> a UUID placeholder on create (reusing the uuid auto-fill) + the series markers.
Map<String, Object> siNumber = propertyByName(entityByName(entities, "SalesInvoice"), "Number");
assertEquals("issue", siNumber.get("numberStampOn"));
assertEquals("SalesInvoice", siNumber.get("numberSeries"));
assertEquals("true", siNumber.get("generatedUuid"));
assertNull(siNumber.get("numberStampOnCreate"));

// stampOn: create -> the real number is stamped on insert (numberStampOnCreate), no placeholder.
Map<String, Object> pfNumber = propertyByName(entityByName(entities, "Proforma"), "Number");
assertEquals("create", pfNumber.get("numberStampOn"));
assertEquals("true", pfNumber.get("numberStampOnCreate"));
assertEquals("PF-{seq:05}", pfNumber.get("numberFormat"));
assertNull(pfNumber.get("generatedUuid"));
}

@Test
void crossModelProjectionCellIsMarkedProjectionInTheEdmDiagram() {
IntentModel parsed = IntentParser.parse(readResource("/billing/customers.intent"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,33 @@ public class ${name}Repository extends JavaRepository<${name}Entity> {
#end
#end
## A uuid field (intent type: uuid) is platform-generated: assign a random UUID on create when empty.
## A number: {} field with stampOn:issue also carries generatedUuid, so its create-time placeholder is
## a UUID (the real number is stamped at the issue step); stampOn:create fields are handled just below.
#foreach ($property in $properties)
#if($property.generatedUuid)
if (entity.${property.name} == null || entity.${property.name}.isBlank()) {
entity.${property.name} = java.util.UUID.randomUUID().toString();
}
#end
#end
## First-class numbering, stampOn:create: allocate + format the real document number on insert (when
## empty), via the shared per-tenant counter. The scope both partitions the counter and feeds the
## format's tokens; `year` is the current year, any other name reads the entity's own field.
#foreach ($property in $properties)
#if($property.numberStampOnCreate)
if (entity.${property.name} == null || entity.${property.name}.isBlank()) {
java.util.Map<String, String> ${property.name}Scope = new java.util.LinkedHashMap<>();
#foreach ($scopeName in $property.numberScope)
#if($scopeName == "year")
${property.name}Scope.put("year", String.valueOf(java.time.Year.now().getValue()));
#else
${property.name}Scope.put("${scopeName}", entity.${scopeName} == null ? "" : String.valueOf(entity.${scopeName}));
#end
#end
entity.${property.name} = org.eclipse.dirigible.sdk.numbering.DocumentNumbers.next("${property.numberSeries}", "${property.numberFormat}", ${property.name}Scope);
}
#end
#end
#if($documentMaster)
recalculate(entity);
#end
Expand Down
Loading