@@ -124,34 +124,12 @@ public async Task<Result> AssignOperatorToMerchant(MerchantCommands.AssignOperat
124124 if ( result . IsFailed )
125125 return ResultHelpers . CreateFailure ( result ) ;
126126
127- // Is the operator valid for this estate
128- Estate estate = estateAggregate . GetEstate ( ) ;
129- Models . Estate . Operator @operator = estate . Operators ? . SingleOrDefault ( o => o . OperatorId == command . RequestDto . OperatorId ) ;
130- if ( @operator == null ) {
131- return Result . Invalid ( $ "Operator Id { command . RequestDto . OperatorId } is not supported on Estate [{ estate . Name } ]") ;
132- }
133-
134- Result < OperatorAggregate > operatorResult = await DomainServiceHelper . GetAggregateOrFailure ( ct => this . AggregateService . GetLatest < OperatorAggregate > ( command . RequestDto . OperatorId , ct ) , command . RequestDto . OperatorId , cancellationToken ) ;
135- if ( operatorResult . IsFailed )
136- return ResultHelpers . CreateFailure ( operatorResult ) ;
137- OperatorAggregate @operatorAggregate = operatorResult . Data ;
138- // Operator has been validated, now check the rules of the operator against the passed in data
139- if ( @operatorAggregate . RequireCustomMerchantNumber ) {
140- // requested addition must have a merchant number supplied
141- if ( String . IsNullOrEmpty ( command . RequestDto . MerchantNumber ) ) {
142- return Result . Invalid ( $ "Operator Id { command . RequestDto . OperatorId } requires that a merchant number is provided") ;
143- }
144- }
145-
146- if ( @operatorAggregate . RequireCustomTerminalNumber ) {
147- // requested addition must have a terminal number supplied
148- if ( String . IsNullOrEmpty ( command . RequestDto . TerminalNumber ) ) {
149- return Result . Invalid ( $ "Operator Id { command . RequestDto . OperatorId } requires that a terminal number is provided") ;
150- }
151- }
127+ ( Result validationResult , String operatorName ) = await this . GetOperatorAssignmentDetails ( command , estateAggregate , cancellationToken ) ;
128+ if ( validationResult . IsFailed )
129+ return validationResult ;
152130
153131 // Assign the operator
154- Result stateResult = merchantAggregate . AssignOperator ( command . RequestDto . OperatorId , @operator . Name , command . RequestDto . MerchantNumber , command . RequestDto . TerminalNumber ) ;
132+ Result stateResult = merchantAggregate . AssignOperator ( command . RequestDto . OperatorId , operatorName , command . RequestDto . MerchantNumber , command . RequestDto . TerminalNumber ) ;
155133 if ( stateResult . IsFailed )
156134 return stateResult ;
157135
@@ -166,6 +144,46 @@ public async Task<Result> AssignOperatorToMerchant(MerchantCommands.AssignOperat
166144 }
167145 }
168146
147+ private async Task < ( Result ValidationResult , String OperatorName ) > GetOperatorAssignmentDetails ( MerchantCommands . AssignOperatorToMerchantCommand command ,
148+ EstateAggregate estateAggregate ,
149+ CancellationToken cancellationToken )
150+ {
151+ Estate estate = estateAggregate . GetEstate ( ) ;
152+ Models . Estate . Operator @operator = estate . Operators ? . SingleOrDefault ( o => o . OperatorId == command . RequestDto . OperatorId ) ;
153+ if ( @operator == null ) {
154+ return ( Result . Invalid ( $ "Operator Id { command . RequestDto . OperatorId } is not supported on Estate [{ estate . Name } ]") , String . Empty ) ;
155+ }
156+
157+ Result < OperatorAggregate > operatorResult = await DomainServiceHelper . GetAggregateOrFailure ( ct => this . AggregateService . GetLatest < OperatorAggregate > ( command . RequestDto . OperatorId , ct ) , command . RequestDto . OperatorId , cancellationToken ) ;
158+ if ( operatorResult . IsFailed )
159+ return ( ResultHelpers . CreateFailure ( operatorResult ) , String . Empty ) ;
160+
161+ Result validationResult = this . ValidateOperatorAssignment ( command . RequestDto . OperatorId ,
162+ command . RequestDto . MerchantNumber ,
163+ command . RequestDto . TerminalNumber ,
164+ operatorResult . Data ) ;
165+
166+ return validationResult . IsFailed
167+ ? ( validationResult , String . Empty )
168+ : ( Result . Success ( ) , @operator . Name ) ;
169+ }
170+
171+ private Result ValidateOperatorAssignment ( Guid operatorId ,
172+ String merchantNumber ,
173+ String terminalNumber ,
174+ OperatorAggregate operatorAggregate )
175+ {
176+ if ( operatorAggregate . RequireCustomMerchantNumber && String . IsNullOrEmpty ( merchantNumber ) ) {
177+ return Result . Invalid ( $ "Operator Id { operatorId } requires that a merchant number is provided") ;
178+ }
179+
180+ if ( operatorAggregate . RequireCustomTerminalNumber && String . IsNullOrEmpty ( terminalNumber ) ) {
181+ return Result . Invalid ( $ "Operator Id { operatorId } requires that a terminal number is provided") ;
182+ }
183+
184+ return Result . Success ( ) ;
185+ }
186+
169187 private SettlementSchedule ConvertSettlementSchedule ( DataTransferObjects . Responses . Merchant . SettlementSchedule settlementSchedule ) =>
170188 settlementSchedule switch
171189 {
@@ -175,6 +193,23 @@ private SettlementSchedule ConvertSettlementSchedule(DataTransferObjects.Respons
175193 _ => SettlementSchedule . NotSet
176194 } ;
177195
196+ private MerchantDepositSource ConvertDepositSource ( DataTransferObjects . Requests . Merchant . MerchantDepositSource depositSource ) =>
197+ depositSource switch
198+ {
199+ DataTransferObjects . Requests . Merchant . MerchantDepositSource . Manual => MerchantDepositSource . Manual ,
200+ _ => MerchantDepositSource . Automatic ,
201+ } ;
202+
203+ private Result EnsureMerchantDepositListCreated ( MerchantDepositListAggregate merchantDepositListAggregate ,
204+ MerchantAggregate merchantAggregate ,
205+ DateTime depositDateTime ) {
206+ if ( merchantDepositListAggregate . IsCreated ) {
207+ return Result . Success ( ) ;
208+ }
209+
210+ return merchantDepositListAggregate . Create ( merchantAggregate , depositDateTime ) ;
211+ }
212+
178213 public async Task < Result > CreateMerchant ( MerchantCommands . CreateMerchantCommand command , CancellationToken cancellationToken )
179214 {
180215 try {
@@ -310,19 +345,12 @@ public async Task<Result> MakeMerchantDeposit(MerchantCommands.MakeMerchantDepos
310345 return ResultHelpers . CreateFailure ( getDepositListResult ) ;
311346
312347 MerchantDepositListAggregate merchantDepositListAggregate = getDepositListResult . Data ;
313- if ( merchantDepositListAggregate . IsCreated == false )
314- {
315- Result createResult = merchantDepositListAggregate . Create ( merchantAggregate , command . RequestDto . DepositDateTime ) ;
316- if ( createResult . IsFailed )
317- return ResultHelpers . CreateFailure ( createResult ) ;
318- }
348+ Result createResult = this . EnsureMerchantDepositListCreated ( merchantDepositListAggregate , merchantAggregate , command . RequestDto . DepositDateTime ) ;
349+ if ( createResult . IsFailed )
350+ return ResultHelpers . CreateFailure ( createResult ) ;
319351
320352 PositiveMoney amount = PositiveMoney . Create ( Money . Create ( command . RequestDto . Amount ) ) ;
321- MerchantDepositSource depositSource = command . DepositSource switch
322- {
323- DataTransferObjects . Requests . Merchant . MerchantDepositSource . Manual => MerchantDepositSource . Manual ,
324- _ => MerchantDepositSource . Automatic ,
325- } ;
353+ MerchantDepositSource depositSource = this . ConvertDepositSource ( command . DepositSource ) ;
326354 Result stateResult = merchantDepositListAggregate . MakeDeposit ( depositSource , command . RequestDto . Reference , command . RequestDto . DepositDateTime , amount ) ;
327355 if ( stateResult . IsFailed )
328356 return ResultHelpers . CreateFailure ( stateResult ) ;
@@ -345,48 +373,17 @@ public async Task<Result> MakeMerchantWithdrawal(MerchantCommands.MakeMerchantWi
345373
346374 try
347375 {
348- Result < EstateAggregate > estateResult = await DomainServiceHelper . GetAggregateOrFailure ( ct => this . AggregateService . Get < EstateAggregate > ( command . EstateId , ct ) , command . EstateId , cancellationToken ) ;
349- if ( estateResult . IsFailed )
350- return ResultHelpers . CreateFailure ( estateResult ) ;
351-
352- Result < MerchantAggregate > merchantResult = await DomainServiceHelper . GetAggregateOrFailure ( ct => this . AggregateService . Get < MerchantAggregate > ( command . MerchantId , ct ) , command . MerchantId , cancellationToken ) ;
353- if ( merchantResult . IsFailed )
354- return ResultHelpers . CreateFailure ( merchantResult ) ;
355-
356- EstateAggregate estateAggregate = estateResult . Data ;
357- MerchantAggregate merchantAggregate = merchantResult . Data ;
358-
359- Result validateResult =
360- this . ValidateEstateAndMerchant ( estateAggregate , merchantAggregate ) ;
361- if ( validateResult . IsFailed )
362- return ResultHelpers . CreateFailure ( validateResult ) ;
363-
364- Result < MerchantDepositListAggregate > getDepositListResult = await DomainServiceHelper . GetAggregateOrFailure ( ct => this . AggregateService . GetLatest < MerchantDepositListAggregate > ( command . MerchantId , ct ) , command . MerchantId , cancellationToken ) ;
376+ Result < MerchantDepositListAggregate > getDepositListResult = await this . GetMerchantDepositListForWithdrawal ( command , cancellationToken ) ;
365377 if ( getDepositListResult . IsFailed )
366378 return ResultHelpers . CreateFailure ( getDepositListResult ) ;
367-
368- MerchantDepositListAggregate merchantDepositListAggregate = getDepositListResult . Data ;
369- if ( merchantDepositListAggregate . IsCreated == false )
370- {
371- return Result . Invalid ( $ "Merchant [{ command . MerchantId } ] has not made any deposits yet") ;
372- }
373379
374- // Now we need to check the merchants balance to ensure they have funds to withdraw
375- Result < String > getBalanceResult = await this . EventStoreContext . GetPartitionStateFromProjection ( "MerchantBalanceProjection" , $ "MerchantBalance-{ command . MerchantId : N} ", cancellationToken ) ;
376- if ( getBalanceResult . IsFailed )
377- {
378- return Result . Invalid ( $ "Failed to get Merchant Balance.") ;
379- }
380-
381- MerchantBalanceProjectionState1 projectionState = JsonConvert . DeserializeObject < MerchantBalanceProjectionState1 > ( getBalanceResult . Data ) ;
382-
383- if ( command . RequestDto . Amount > projectionState . merchant . balance )
384- {
385- return Result . Invalid ( $ "Not enough credit available for withdrawal of [{ command . RequestDto . Amount } ]. Balance is { projectionState . merchant . balance } ") ;
386- }
380+ Result validateBalanceResult = await this . ValidateWithdrawalBalance ( command , cancellationToken ) ;
381+ if ( validateBalanceResult . IsFailed )
382+ return validateBalanceResult ;
387383
388384 // If we are here we have enough credit to withdraw
389385 PositiveMoney amount = PositiveMoney . Create ( Money . Create ( command . RequestDto . Amount ) ) ;
386+ MerchantDepositListAggregate merchantDepositListAggregate = getDepositListResult . Data ;
390387
391388 Result stateResult = merchantDepositListAggregate . MakeWithdrawal ( command . RequestDto . WithdrawalDateTime , amount ) ;
392389 if ( stateResult . IsFailed )
@@ -716,6 +713,57 @@ private Result ValidateEstateAndMerchant(EstateAggregate estateAggregate,
716713 return Result . Success ( ) ;
717714 }
718715
716+ private async Task < Result < MerchantDepositListAggregate > > GetMerchantDepositListForWithdrawal ( MerchantCommands . MakeMerchantWithdrawalCommand command ,
717+ CancellationToken cancellationToken )
718+ {
719+ Result < EstateAggregate > estateResult = await DomainServiceHelper . GetAggregateOrFailure ( ct => this . AggregateService . Get < EstateAggregate > ( command . EstateId , ct ) , command . EstateId , cancellationToken ) ;
720+ if ( estateResult . IsFailed )
721+ return ResultHelpers . CreateFailure ( estateResult ) ;
722+
723+ Result < MerchantAggregate > merchantResult = await DomainServiceHelper . GetAggregateOrFailure ( ct => this . AggregateService . Get < MerchantAggregate > ( command . MerchantId , ct ) , command . MerchantId , cancellationToken ) ;
724+ if ( merchantResult . IsFailed )
725+ return ResultHelpers . CreateFailure ( merchantResult ) ;
726+
727+ Result validateResult = this . ValidateEstateAndMerchant ( estateResult . Data , merchantResult . Data ) ;
728+ if ( validateResult . IsFailed )
729+ return ResultHelpers . CreateFailure ( validateResult ) ;
730+
731+ Result < MerchantDepositListAggregate > getDepositListResult = await DomainServiceHelper . GetAggregateOrFailure ( ct => this . AggregateService . GetLatest < MerchantDepositListAggregate > ( command . MerchantId , ct ) , command . MerchantId , cancellationToken ) ;
732+ if ( getDepositListResult . IsFailed )
733+ return ResultHelpers . CreateFailure ( getDepositListResult ) ;
734+
735+ MerchantDepositListAggregate merchantDepositListAggregate = getDepositListResult . Data ;
736+ if ( merchantDepositListAggregate . IsCreated == false )
737+ {
738+ return Result . Invalid ( $ "Merchant [{ command . MerchantId } ] has not made any deposits yet") ;
739+ }
740+
741+ return Result . Success ( merchantDepositListAggregate ) ;
742+ }
743+
744+ private async Task < Result > ValidateWithdrawalBalance ( MerchantCommands . MakeMerchantWithdrawalCommand command ,
745+ CancellationToken cancellationToken )
746+ {
747+ Result < String > getBalanceResult = await this . EventStoreContext . GetPartitionStateFromProjection ( "MerchantBalanceProjection" , $ "MerchantBalance-{ command . MerchantId : N} ", cancellationToken ) ;
748+ if ( getBalanceResult . IsFailed )
749+ {
750+ return Result . Invalid ( $ "Failed to get Merchant Balance.") ;
751+ }
752+
753+ MerchantBalanceProjectionState1 projectionState = JsonConvert . DeserializeObject < MerchantBalanceProjectionState1 > ( getBalanceResult . Data ) ;
754+ if ( projectionState ? . merchant == null )
755+ {
756+ return Result . Invalid ( "Merchant Balance data is missing or invalid." ) ;
757+ }
758+
759+ if ( command . RequestDto . Amount > projectionState . merchant . balance )
760+ {
761+ return Result . Invalid ( $ "Not enough credit available for withdrawal of [{ command . RequestDto . Amount } ]. Balance is { projectionState . merchant . balance } ") ;
762+ }
763+
764+ return Result . Success ( ) ;
765+ }
766+
719767 public async Task < Result > SwapMerchantDevice ( MerchantCommands . SwapMerchantDeviceCommand command ,
720768 CancellationToken cancellationToken )
721769 {
0 commit comments