# system.kanoa.operators.assignCrewToShift(crewAssignmentInfo, userId)

Assigns an entire crew to a shift by inserting one sch.operatorShiftAssignment row per user who was
an active crewMember as of startDate - not whoever is currently on the crew. crewId is stamped on
each inserted row for provenance/reporting.
startDate/endDate are the assignment's own authoritative boundaries and must be supplied by the
caller - this function does not derive them from sch.shiftBlock or sch.shiftEvent, so OLE/POLE
analysis stays decoupled from however shifts are currently generated/implemented.

---
### Parameters
**crewAssignmentInfo** `Dictionary`<br />
<table class='dictionaryKeyTable'>
	<tbody>
 	<tr><td> - assetId</td><td>Integer</td><td>Not required. Ignored if shiftId is supplied (assetId is then resolved from sch.shift)</td></tr>	
 	<tr><td> - crewId</td><td>Integer</td><td></td></tr>	
 	<tr><td> - endDate</td><td>String</td><td>Not required. The assignment's actual end (open ended if omitted)</td></tr>	
 	<tr><td> - shiftId</td><td>Integer</td><td>Not required. The sch.shift this assignment is tied to</td></tr>	
 	<tr><td> - startDate</td><td>String</td><td>the assignment's actual start</td></tr>	
	</tbody>
</table>

**userId** `Integer`: the actor performing this action (createdBy)<br />

---
### Returns
**rowsInserted** `Integer` number of operatorShiftAssignment rows created, None on failure<br />
**message** `String` None on success, an error message on failure - including when no crew member<br />
**was active as of startDate** `rowsInserted is 0 in that case, not a silent no-op`<br />

---
### Example
```python
crewAssignmentInfo = {'crewId': 5,	'assetId': 4, shiftId: 7, 'startDate': system.date.now(), 'endDate': None}
userId = 12
rowsAffected, message = system.kanoa.operators.assignCrewToShift(sessionInfo, userId)
```
