As @svaj says, you can get sequential numbers either for customers or orders by using custom objects. The custom object will act as lock thanks to the version, avoiding that two concurrent requests could get the same number. You can also use recursion to try again in the event of a concurrency error.
This is a working example in ES6 (the same applies for order number). The only thing you have to set is the name of the sequence that will be the container and key of the custom object (For example "customerSequence").
setCustomerNumber({ sequence, value, version }) {
return customObjectsService
.save({
container: sequence,
key: sequence,
value,
version,
})
.then(customObject => customObject.value);
},
getCustomerNumber(sequence) {
return customObjectsService
.find({ where: `key="${sequence}"` })
.then(({ results }) => (results.length ? results[0] : { value: 0 }))
.then(lastValue => {
return this.setCustomerNumber({
sequence,
value: lastValue.value + 1,
version: lastValue.version,
}).catch(() => this.getCustomerNumber(sequence)); // We request a new one on error
});
},
And here you have the unit test of the previous code
describe('when getting the next customer number', () => {
const sequence = 'customersSequence';
describe('when existing previous customer number', () => {
const oldCustomerNumber = 1;
const newCustomerNumber = oldCustomerNumber + 1;
const version = 1;
beforeEach(() => {
spyOn(customObjectsService, 'find').and.returnValue(
Promise.resolve({
results: [
{
value: oldCustomerNumber,
version,
},
],
total: 1,
}),
);
spyOn(customersService, 'setCustomerNumber').and.returnValue(
Promise.resolve(newCustomerNumber),
);
});
it('should get a customer number equals to <previous customer number> + 1', done => {
customersService
.getCustomerNumber(sequence)
.then(customerNumber => {
expect(customObjectsService.find).toHaveBeenCalledWith({
where: `key="${sequence}"`,
});
expect(customersService.setCustomerNumber).toHaveBeenCalledWith({
sequence,
value: newCustomerNumber,
version,
});
expect(customerNumber).toBe(newCustomerNumber);
})
.then(done, done.fail);
});
});
describe('when NOT existing previous customer number', () => {
const newCustomerNumber = 1;
beforeEach(() => {
spyOn(customObjectsService, 'find').and.returnValue(
Promise.resolve({
results: [],
total: 0,
}),
);
spyOn(customersService, 'setCustomerNumber').and.returnValue(
Promise.resolve(newCustomerNumber),
);
});
it('should get a customer number equals to 1', done => {
customersService
.getCustomerNumber(sequence)
.then(customerNumber => {
expect(customObjectsService.find).toHaveBeenCalledWith({
where: `key="${sequence}"`,
});
expect(customersService.setCustomerNumber).toHaveBeenCalledWith({
sequence,
value: newCustomerNumber,
version: undefined,
});
expect(customerNumber).toBe(newCustomerNumber);
})
.then(done, done.fail);
});
});
});