Model 109 (after 108)

Changes

application language

'Orders': collection ['Order'] {
	'Order': text
	'Order Date': number 'date'
}
'Period Size': number positive 'days'
'Periods': collection ['Period']
	'Period Order': ordered-graph .'First' ( ?'Yes' || ?'No'>'Predecessor' ) = partition
	= partition .'Period Size' as 'Period Start' in 'Period Order' (
		'1' = .'Orders'* .'Order Date'
	) {
	'Period': text = key
	'Period Start': number 'date' = parameter
	'First': stategroup = parameter (
		'No' {
			'Predecessor': text -> ^ sibling in ( 'Period Order' ) = parameter
		}
		'Yes' { }
	)
	'Orders': reference-set -> ^ .'Orders'* = branch '1'
}
'Tasks': collection ['Task']
	'Dependencies': acyclic-graph
{
	'Task': text
	'Predecessor': stategroup (
		'None' { }
		'After' {
			'Task': text -> ^ sibling in ( 'Dependencies' )
		}
	)
}
'Milestones': collection ['Task']
	'Milestone Order': acyclic-graph = 'Dependencies'
{
	'Task': text -> ^ .'Tasks'[]
	'Predecessor': stategroup = on >'Task' as $'Step' in 'Dependencies' => switch $'Step'.'Predecessor' (
		|'None' => 'None' ( )
		|'After' as $ => switch sibling in ( 'Milestone Order' ) [ $ >'Task'] (
			| node as $ => 'After' ( 'Task' = $ )
			| none => recurse on $^ $'Step' = $ >'Task'
		)
	) (
		'None' { }
		'After' {
			'Task': text -> ^ sibling in ( 'Milestone Order' ) = parameter
		}
	)
}
'Work Centers': collection ['Center'] {
	'Center': text
	'Shifts': collection ['Shift'] {
		'Shift': text
		'Time Slots': collection ['Slot'] {
			'Slot': text
		}
	}
}
'Work Center': text -> .'Work Centers'[]
'Assignments': collection ['Assignment'] {
	'Assignment': text
	'Shift': text -> ^ >'Work Center'.'Shifts'[]
	'Time Slot': text -> >'Shift'.'Time Slots'[]
	'Available Shifts': collection ['Shift'] = union (
		'1' = >'Shift'
	) {
		'Shift': text -> ^ ^ >'Work Center'.'Shifts'[] = key
		'1': reference-set -> ^ = branch '1' // NOTE: no collection steps, only a parent step
		'Available Slots': collection ['Slot'] = union (
			'1' = <'1'* >'Time Slot' // not supported in model 108
		) {
			'Slot': text -> ^ >'Shift'.'Time Slots'[] = key
		}
	}
}
'Favorite Product Ordered': stategroup = switch .'Ordered Products'>[ >'Favorite Product' ] (
	| node => 'Yes' ( )
	| none => 'No' ( )
) ( ... )
'Product': text -> ^ .'Products'[] as $
	where 'Ordered' -> ^ .'Ordered Products'>[ $ ]
// old
'Parts Cost': number 'euro' = sum .'Parts'* .'Part Price'
'# Products': number 'items' = count .'Products'*

// new
'Parts Cost': number 'euro' = sum ( .'Parts'* .'Part Price' )
'# Products': number 'items' = count ( .'Products'* )
// old
'Previous Year': text -> ^ sibling in ( 'Ordered Years' )
'Self or Sibling': text -> ^ sibling || self

// new
'Previous Year': text -> ^ siblings in 'Ordered Years' []
'Self or Sibling': text -> ^ siblings || self []
'Weeks': reference-set -> ^ siblings * .'Weeks'*
'Open Orders': stategroup = switch .'Orders'* .'Status'?'Open' (
	| nodes as $ => 'Several' ( '# Orders' = count ( $ * ) )
	| node       => 'One' ( )
	| none       => 'None' ( )
) ( ... )
'# Deliveries': number 'items' = count ( .'Orders'* .'Lines'* >'Product'.'Deliveries'* )
'Lines': reference-set -> downstream ^ ^ .'Orders'>[ ^ ] .'Lines'* = inverse >'Order'
'# Members': number 'items' = count ( >'Region' <'Customers'[ >'Country' .'Cities'* .'Customers'* ] )
'# In Graph': number 'items' = count ( <'Slots'[ siblings in 'Slot Order' * ] )

// ERROR: a filter that the signature of 'Customers' does not declare
'# Active': number 'items' = count ( >'Region' <'Customers'[ >'Country' .'Cities'* .'Customers'* .'Status'?'Active' ] )

A level of a subset path may not be a reference set or a matched set, and a sibling or graph step is allowed only where the signature has the same location. Below a level that the path pins, the filters of the signature may be left out. A subset step produces every node once, also when several members of the set lead to it.

'Line found': stategroup = switch <'Lines'[ ^ >'Order'.'Lines'* ] ( // 'Line' is the key of 'Lines'
	| node => 'Yes' ( )
	| none => 'No' ( )
) ( ... )
'Debtors': collection ['Debtor'] = union (
	'' = .'Orders'* as $ on $ >'Debtor' // group: one entry per debtor
) { ... }

'Slots': collection ['Slot'] = union (
	'this' = <'Shifts'[ ^ .'Time Slots'* ] // merge: one entry per node the path produces
) { ... }
'Orders': collection ['Order'] {
	'Order': text -> ^ .'Order Numbers'[] as $
		where 'Invoice' -> ^ .'Invoices'>[ $ ] // unique lookup value: distinct orders reach distinct invoices
}
'Debtors': collection ['Debtor'] = union (
	'this' = .'Orders'* as $ on $ .'Order'&'Invoice'>'Debtor' // accepted
) { ... }

The lookup value decides, not whether the property holding the rule is the key of its collection. A lookup on a value that repeats, or into a collection that lives inside the entry itself, does not re-root the scope.

'Key B': text -> ^ >'Ref A' .'Status'?'Open' ^ .'B'[] = key

'ok'  = .'Items'* as $ on $ >'Ref B'  // >'Ref B' takes the same filter
'bad' = .'Items'* as $ on $ >'Ref B2' // ERROR: the filter ?'Open' is missing

The reverse is allowed: a value path may filter where the reference does not.

'Ref': text -> siblings [] as $
	where 'in S' -> $ .'Status'?'Open'
'Ref in S': text -> siblings [] .'Status'?'Open' = .'Ref'&'in S' // accepted

'Lines': collection ['Line'] = flatten (
	'open' = .'Orders'* .'Status'?'Open' as $ ( 'Debtor' = $ ^ ^ >'Debtor' )
) join - { ... }
'Steps': collection ['Step']
	'Order': acyclic-graph = 'Deps' // ERROR when 'Deps' is defined on a nested collection

'Periods': collection ['Period'] = partition ... {
	'First': stategroup = parameter (
		'No' {
			'Predecessor': text -> ^ siblings in 'Period Order' [] = parameter // ERROR: the partition initializes the entry
		}
		'Yes' { }
	)
}
'First Activity': text -> downstream ^ .'Activities'[] = sink-of ^ .'Activities'* in 'Order' // accepted

// ERROR: a state filter in the collection path
'First Open': text -> downstream ^ .'Activities'[] = sink-of ^ .'Activities'* .'Status'?'Open' in 'Order'

// ERROR: a reference set holds some entries, not a whole collection
'First Member': text -> downstream ^ .'Activities'[] = sink-of <'Members'* in 'Order'
'Mode': stategroup (
	'Normal' { }
	'Urgent' { }
)
'Theme color': text
'Orders': collection ['Number'] {
	'Number': text
	'Colour': text
	'Status': stategroup (
		'Open'
			has-todo: user
				@description: "Handle this order"
				@style: switch .'Mode' ( // accepted: 'Mode' is a property of the root
					|'Normal' => foreground
					|'Urgent' => error
				)
		{ }
		'Closed'
			has-todo: user @style: to-color .'Theme color' // accepted: 'Theme color' is a property of the root
		{ }
	)
}
'Deliveries': collection ['Number'] {
	'Number': text
	'Colour': text
	'Status': stategroup (
		'Open'
			has-todo: user
				@style: to-color .'Colour' // ERROR: 'property' `Colour` was not found in 'attributes'
		{ }
		'Closed' { }
	)
}
'Company': text
'Orders': collection ['Number'] {
	'Number': text
	'Status': stategroup (
		'Open'
			has-todo: user
				@label: concat ( "Open order of ", .'Company' ) // accepted: 'Company' is a property of the root
				@description: "Handle this order"
				@style: warning
		{ }
		'Closed' { }
	)
}
'Deliveries': collection ['Number'] {
	'Number': text
	'Status': stategroup (
		'Open'
			has-todo: user
				@label: .'Number' // ERROR: 'property' `Number` was not found in 'attributes'
		{ }
		'Closed' { }
	)
}
'Problemen': collection ['Probleem'] {
	'Probleem': text
	'Afgehandeld': stategroup (
		'Nee'
			has-todo: user @description: "Probleem: los dit op in de betreffende module."
		{ }
		'Ja' { }
	)
}
'Openstaande problemen': collection ['Probleem'] = .'Problemen'* .'Afgehandeld'?'Nee' {
	'Probleem': text -> ^ .'Problemen'[] .'Afgehandeld'?'Nee' = key
	'Gemeld': stategroup = 'Nee' ( ) (
		'Nee'
			has-todo: user @description: "Taak: meld dit probleem."
		{ }
		'Ja' { }
	)
}

A has-todo: inside a node body no longer parses. The upgrade transformations carry a todo that already sat on a state to its new place. A todo on a root, group or collection node moves to the one state of a new derived stategroup 'Todo item', the node’s last property, hidden from the views; its requirement gets a leading ^ on every path that started at the node, because it now starts at the state’s node:

'Problemen': collection ['Probleem'] {
	'Probleem': text
	'Todo item': stategroup = 'Yes' ( ) @hidden (
		'Yes'
			has-todo: user where ( ^ .'Status'?'Open' ) @description: "Probleem: los dit op in de betreffende module."
		{ }
	)
}

Replace it with a stategroup of your own when the todo should end, as in the example above. A todo on the parameters of a command or action has no derived stategroup to carry it and is dropped. A node that already has a property 'Todo item' cannot take the new one: rename that property before you upgrade.