From 63bafe7720cbff4001eaa77a799783b80c4bb8e6 Mon Sep 17 00:00:00 2001 From: mr-impossibru <64555470+mr-impossibru@users.noreply.github.com> Date: Thu, 26 May 2022 14:23:33 +0300 Subject: [PATCH 01/36] ft/TD-249/optimize db schema (#48) * TD-249: optimise db schema, refactor, temporarily rename to daway --- .github/workflows/build.yml | 2 +- .github/workflows/deploy.yml | 1 + pom.xml | 2 +- .../newway/config/ApplicationConfig.java | 4 +- .../dev/vality/newway/config/CacheConfig.java | 18 +- .../vality/newway/config/SchedulerConfig.java | 2 +- .../dao/invoicing/iface/CashFlowLinkDao.java | 22 + .../dao/invoicing/iface/InvoiceCartDao.java | 5 +- .../dao/invoicing/iface/InvoiceDao.java | 3 - .../invoicing/iface/InvoiceStatusInfoDao.java | 18 + .../iface/PaymentAdditionalInfoDao.java | 19 + .../dao/invoicing/iface/PaymentDao.java | 5 - .../dao/invoicing/iface/PaymentFeeDao.java | 19 + .../invoicing/iface/PaymentPayerInfoDao.java | 16 + .../iface/PaymentRecurrentInfoDao.java | 19 + .../invoicing/iface/PaymentRiskDataDao.java | 19 + .../dao/invoicing/iface/PaymentRouteDao.java | 19 + .../invoicing/iface/PaymentStatusInfoDao.java | 18 + .../invoicing/impl/CashFlowLinkDaoImpl.java | 124 + ...a => CashFlowLinkIdsGeneratorDaoImpl.java} | 4 +- .../invoicing/impl/InvoiceCartDaoImpl.java | 28 +- .../dao/invoicing/impl/InvoiceDaoImpl.java | 31 +- .../impl/InvoiceIdsGeneratorDaoImpl.java | 29 - .../impl/InvoiceStatusInfoDaoImpl.java | 87 + .../impl/PaymentAdditionalInfoDaoImpl.java | 95 + .../dao/invoicing/impl/PaymentDaoImpl.java | 47 +- .../dao/invoicing/impl/PaymentFeeDaoImpl.java | 94 + .../impl/PaymentPayerInfoDaoImpl.java | 60 + .../impl/PaymentRecurrentInfoDaoImpl.java | 93 + .../impl/PaymentRiskDataDaoImpl.java | 94 + .../invoicing/impl/PaymentRouteDaoImpl.java | 94 + .../impl/PaymentStatusInfoDaoImpl.java | 95 + .../dao/invoicing/impl/RefundDaoImpl.java | 18 +- .../dao/party/impl/RevisionDaoImpl.java | 12 +- .../newway/dao/rate/impl/RateDaoImpl.java | 4 +- .../factory/cash/flow/CashFlowFactory.java | 69 + .../cash/flow/CashFlowLinkFactory.java | 26 + .../contractor/ContractorFactory.java} | 14 +- .../InvoicePaymentEventIdHolderFactory.java | 20 + .../invoice/payment/PaymentFeeFactory.java | 37 + .../invoice/payment/PaymentRouteFactory.java | 29 + .../payment/PaymentStatusInfoFactory.java | 45 + ...AdjustmentMachineEventCopyFactoryImpl.java | 2 +- .../ChallengeMachineEventCopyFactoryImpl.java | 2 +- ...ChargebackMachineEventCopyFactoryImpl.java | 2 +- ...tAjustmentMachineEventCopyFactoryImpl.java | 2 +- .../DepositMachineEventCopyFactoryImpl.java | 2 +- ...ositRevertMachineEventCopyFactoryImpl.java | 2 +- ...estinationMachineEventCopyFactoryImpl.java | 2 +- .../IdentityMachineEventCopyFactoryImpl.java | 2 +- .../event}/MachineEventCopyFactory.java | 2 +- .../PartyMachineEventCopyFactoryImpl.java | 2 +- .../RecurrentPaymentToolCopyFactoryImpl.java | 2 +- .../RefundMachineEventCopyFactoryImpl.java | 2 +- .../SourceMachineEventCopyFactoryImpl.java | 2 +- .../WalletMachineEventCopyFactoryImpl.java | 2 +- ...WithdrawalMachineEventCopyFactoryImpl.java | 2 +- ...walSessionMachineEventCopyFactoryImpl.java | 2 +- .../handler/event/stock/LocalStorage.java | 18 - .../impl/deposit/DepositCreatedHandler.java | 2 +- .../deposit/DepositStatusChangedHandler.java | 2 +- .../DepositTransferCreatedHandler.java | 2 +- .../DepositTransferStatusChangedHandler.java | 2 +- .../DepositAdjustmentCreatedHandler.java | 2 +- ...DepositAdjustmentStatusChangedHandler.java | 2 +- ...positAdjustmentTransferCreatedHandler.java | 2 +- ...djustmentTransferStatusChangedHandler.java | 2 +- .../revert/DepositRevertCreatedHandler.java | 2 +- .../DepositRevertStatusChangedHandler.java | 2 +- .../DepositRevertTransferCreatedHandler.java | 2 +- ...sitRevertTransferStatusChangedHandler.java | 2 +- .../DestinationAccountCreatedHandler.java | 2 +- .../DestinationCreatedHandler.java | 2 +- .../DestinationStatusChangedHandler.java | 2 +- .../IdentityChallengeCreatedHandler.java | 2 +- ...IdentityChallengeStatusChangedHandler.java | 2 +- .../impl/identity/IdentityCreatedHandler.java | 2 +- ...ntityEffectiveChallengeChangedHandler.java | 2 +- .../identity/IdentityLevelChangedHandler.java | 2 +- ...nvoicePaymentAdjustmentCreatedHandler.java | 8 +- ...PaymentAdjustmentStatusChangedHandler.java | 2 +- ...cePaymentChargebackBodyChangedHandler.java | 2 +- ...ymentChargebackCashFlowChangedHandler.java | 6 +- ...nvoicePaymentChargebackCreatedHandler.java | 2 +- ...cePaymentChargebackLevyChangedHandler.java | 2 +- ...ePaymentChargebackStageChangedHandler.java | 2 +- ...PaymentChargebackStatusChangedHandler.java | 2 +- .../InvoicePaymentRefundCreatedHandler.java | 8 +- ...dSessionChangeTransactionBoundHandler.java | 2 +- ...oicePaymentRefundStatusChangedHandler.java | 2 +- .../contract/ContractCreatedHandler.java | 4 +- .../contractor/ContractorCreatedHandler.java | 4 +- .../party/PartyBlockingHandler.java | 2 +- .../party/PartyCreatedHandler.java | 2 +- .../party/PartyMetaSetHandler.java | 2 +- .../party/PartyRevisionChangedHandler.java | 2 +- .../party/PartySuspensionHandler.java | 2 +- ...currentPaymentToolHasAbandonedHandler.java | 2 +- ...ecurrentPaymentToolHasAcquiredHandler.java | 2 +- ...RecurrentPaymentToolHasCreatedHandler.java | 2 +- .../RecurrentPaymentToolHasFailedHandler.java | 2 +- ...entPaymentToolRiskScoreChangedHandler.java | 2 +- ...currentPaymentToolRouteChangedHandler.java | 2 +- ...SessionChangedTransactionBoundHandler.java | 2 +- .../source/SourceAccountCreatedHandler.java | 2 +- .../impl/source/SourceCreatedHandler.java | 2 +- .../source/SourceStatusChangedHandler.java | 2 +- .../wallet/WalletAccountCreatedHandler.java | 2 +- .../impl/wallet/WalletCreatedHandler.java | 2 +- .../withdrawal/WithdrawalCreatedHandler.java | 2 +- .../WithdrawalRouteChangeHandler.java | 2 +- .../WithdrawalStatusChangedHandler.java | 2 +- .../WithdrawalTransferCreatedHandler.java | 2 +- ...ithdrawalTransferStatusChangedHandler.java | 2 +- .../WithdrawalSessionCreatedHandler.java | 2 +- .../WithdrawalSessionFinishedHandler.java | 2 +- .../WithdrawalSessionNextStateHandler.java | 2 +- ...hdrawalSessionTransactionBoundHandler.java | 2 +- .../handler/wrapper/WrapperHandler.java | 11 + .../invoice/InvoiceCartWrapperHandler.java | 47 + .../InvoiceStatusInfoWrapperHandler.java | 53 + .../invoice/InvoiceWrapperHandler.java | 36 + .../payment/CashFlowWrapperHandler.java | 96 + .../PaymentAdditionalInfoWrapperHandler.java | 37 + .../payment/PaymentFeeWrapperHandler.java | 37 + .../PaymentPayerInfoWrapperHandler.java | 35 + .../PaymentRecurrentInfoWrapperHandler.java | 37 + .../PaymentRiskDataWrapperHandler.java | 37 + .../payment/PaymentRouteWrapperHandler.java | 37 + .../PaymentStatusInfoWrapperHandler.java | 37 + .../payment/PaymentWrapperHandler.java | 35 + .../mapper/AbstractInvoicingMapper.java | 7 - .../java/dev/vality/newway/mapper/Mapper.java | 9 +- .../AbstractInvoicingInvoiceMapper.java | 17 - .../mapper/invoice/InvoiceCreatedMapper.java | 104 +- .../invoice/InvoiceStatusChangedMapper.java | 44 +- .../AbstractInvoicingPaymentMapper.java | 32 - .../InvoicePaymentCaptureStartedMapper.java | 62 +- .../InvoicePaymentCashFlowChangedMapper.java | 53 +- .../payment/InvoicePaymentCreatedMapper.java | 249 +- .../InvoicePaymentRecTokenAcquiredMapper.java | 39 +- .../InvoicePaymentRiskScoreChangedMapper.java | 37 +- .../InvoicePaymentRouteChangedMapper.java | 45 +- ...ntSessionChangeTransactionBoundMapper.java | 75 +- .../InvoicePaymentStatusChangedMapper.java | 72 +- .../newway/{util => model}/CashFlowType.java | 6 +- .../vality/newway/model/CashFlowWrapper.java | 17 + .../model/InvoicePaymentEventIdHolder.java | 11 + .../vality/newway/model/InvoiceWrapper.java | 21 +- .../dev/vality/newway/model/PartyShop.java | 12 + .../vality/newway/model/PaymentWrapper.java | 35 +- .../newway/service/CashFlowService.java | 7 +- .../newway/service/InvoiceBatchService.java | 52 - .../newway/service/InvoiceWrapperService.java | 51 +- .../newway/service/InvoicingService.java | 103 +- .../newway/service/PartyShopCacheService.java | 49 + .../newway/service/PaymentBatchService.java | 40 - .../newway/service/PaymentSquashService.java | 74 - .../newway/service/PaymentWrapperService.java | 85 +- .../dev/vality/newway/util/CashFlowUtil.java | 59 +- .../newway/util/PaymentWrapperUtil.java | 21 + src/main/resources/application.yml | 15 +- src/main/resources/banner.png | Bin 109929 -> 0 bytes src/main/resources/banner.txt | 15 + .../db/migration/V10__commissions.sql | 12 - .../V11__add_recurrent_payer_type.sql | 1 - .../db/migration/V12__recurrents.sql | 4 - .../V13__party_revision_to_refunds.sql | 2 - .../db/migration/V14__session_info_remove.sql | 21 - .../db/migration/V15__1.0.20_fistful_data.sql | 115 - ...destination_and_source_event_sink_data.sql | 123 - .../V17__1.0.25_add_withdrawal_session.sql | 48 - .../db/migration/V18__1.0.26_add_rate.sql | 24 - .../migration/V19__1.0.27_payout_wallet.sql | 7 - src/main/resources/db/migration/V1__init.sql | 2168 +++++++++++++---- .../V20__1.0.28_fix_withdrawal_session.sql | 7 - ...1.0.33_remove_unused_columns_in_payout.sql | 2 - ...35_remove_revision_not_null_constraint.sql | 3 - ....0.36_add_wallet_payout_tool_info_type.sql | 1 - ...36_add_wallet_payout_tool_info_columns.sql | 1 - .../migration/V25__1.0.37_add_payout_type.sql | 1 - .../V26__1.0.39_actualized_fistful_proto.sql | 16 - .../V27__1.0.43_add_payment_method_type.sql | 1 - ...__1.0.44_add_change_id_remove_event_id.sql | 19 - ...29__1.0.46_add_additional_payment_info.sql | 12 - .../V2__cash_flow_aggregate_functions.sql | 436 ---- .../V30__1.0.46_crypto_payment_tool_type.sql | 1 - .../migration/V31__1.0.46_crypto_wallet.sql | 7 - ...dd_resource_type_in_withdrawal_session.sql | 8 - .../V33__1.0.51_add_adjustment_status.sql | 1 - .../V34__1.0.52_update_fistful_proto.sql | 16 - .../V35__1.0.53_xrates_sequence_id.sql | 6 - .../V36__add_capture_started_reason.sql | 1 - .../V37__add_mobile_commerce_type.sql | 1 - .../V38__add_payment_mobile_commerce.sql | 7 - .../V39__add_payment_method_mobile.sql | 2 - .../db/migration/V3__party_revision.sql | 5 - .../db/migration/V40__1.0.56_batch.sql | 14 - .../V41__1.0.58_recurrent_payment_tool.sql | 57 - .../db/migration/V42__1.0.60_external_id.sql | 8 - .../migration/V43__1.0.61_drop_not_null.sql | 2 - ...__1.0.62_add_payment_bank_info_columns.sql | 2 - ...V45__1.0.62_add_payment_system_in_rate.sql | 4 - .../V46__1.0.64_withdrawal_provider.sql | 19 - ...__1.0.65_add_payment_status_adjustment.sql | 1 - .../V48__1.0.66_add_sequence_change_id.sql | 18 - ...49__1.0.67_remove_rec_paytool_event_id.sql | 3 - ...optimize_cash_flow_aggregate_functions.sql | 517 ---- .../db/migration/V50__1.0.68_add_revision.sql | 8 - .../db/migration/V51__cntrct_seq.sql | 5 - .../db/migration/V52__drop_index.sql | 11 - .../db/migration/V53__drop_fk_party.sql | 2 - .../db/migration/V54__add_ids_contr.sql | 8 - .../db/migration/V55__drop_cntrct_seq.sql | 1 - .../db/migration/V56__revision_tables.sql | 39 - .../db/migration/V57.1__add_chargeback.sql | 1 - .../db/migration/V57.2__add_chargeback.sql | 44 - .../V58__drop_f_payment_system_rates.sql | 4 - .../db/migration/V59__revert_V58.sql | 4 - .../db/migration/V5__domain_objects.sql | 11 - .../V60__add_adjustment_cashflow_amount.sql | 17 - .../V61__drop_f_payment_system_rates.sql | 8 - .../V62__new_withdrawal_provider_id.sql | 6 - .../migration/V63__optional_abs_account.sql | 1 - .../migration/V64__optional_terminal_json.sql | 1 - .../V65__add_payer_cardholder_name.sql | 1 - .../V66__unied_provider-n-ext-term.sql | 7 - .../V67__add_terminal_provider_ref.sql | 1 - ...68__remove_not_null_contraint_terminal.sql | 1 - .../V69__add_charged_back_payment_status.sql | 1 - .../db/migration/V6__1.0.10_swift_data.sql | 47 - .../V71__add_payment_routing_rules_table.sql | 12 - ...ange_type_for_routing_decisions_column.sql | 2 - ..._rename_rule_id_and_add_version_column.sql | 2 - .../V74__change_fileds_for_new_seq_scheme.sql | 34 - .../V75__add_new_payment_systems.sql | 3 - .../V76__add_uzcard_payment_system.sql | 1 - .../V77__add_deposit_revert_type.sql | 1 - .../V78__add_deposit_adjustment_type.sql | 1 - ...__revert_and_adjustment_deposit_tables.sql | 56 - .../db/migration/V7__domain_objects.sql | 199 -- .../V80__change_payment_method_ids.sql | 2 - ...V81__fix_deposit_adjustment_and_revert.sql | 17 - ...2__add_crypto_currency_deprecated_type.sql | 1 - .../V83__change_mobile_operator_type.sql | 5 - .../V84__add_country_and_trade_bloc.sql | 29 - .../db/migration/V85__fix_not_req_field.sql | 2 - .../V86__destination_type_digital_wallet.sql | 1 - .../V87__destination_digital_wallet.sql | 5 - .../db/migration/V88__new_payouts.sql | 23 - .../V89__add_payout_tool_info_type.sql | 1 - .../db/migration/V8__session_change.sql | 25 - ...V90__drop_non_null_provider_json_field.sql | 1 - .../V91__drop_cls_field_identity.sql | 2 - .../db/migration/V92__add_schedlock.sql | 8 - .../db/migration/V93__add_adjustmen_diffs.sql | 4 - .../db/migration/V94__add_generic_type.sql | 2 - ..._truncate_party_mngmnt_for_contractors.sql | 1 - .../dev/vality/newway/IntegrationTest.java | 373 ++- .../vality/newway/dao/ChallengeDaoTest.java | 2 +- .../java/dev/vality/newway/dao/DaoTests.java | 612 ++--- .../dev/vality/newway/dao/DepositDaoTest.java | 2 +- .../vality/newway/dao/DestinationDaoTest.java | 2 +- .../vality/newway/dao/IdentityDaoTest.java | 2 +- .../dev/vality/newway/dao/PayoutDaoTest.java | 2 +- .../dev/vality/newway/dao/SourceDaoTest.java | 2 +- .../dev/vality/newway/dao/WalletDaoTest.java | 2 +- .../vality/newway/dao/WithdrawalDaoTest.java | 2 +- .../newway/dao/WithdrawalSessionDaoTest.java | 2 +- .../PartyRevisionChangedHandlerTest.java | 6 +- ...DestinationCreatedBankCardHandlerTest.java | 2 +- ...inationCreatedCryptoWalletHandlerTest.java | 2 +- ...nationCreatedDigitalWalletHandlerTest.java | 2 +- ...awalSessionCreatedBankCardHandlerTest.java | 2 +- ...SessionCreatedCryptoWalletHandlerTest.java | 2 +- ...essionCreatedDigitalWalletHandlerTest.java | 2 +- .../vality/newway/kafka/KafkaProducer.java | 3 +- .../listener/InvoicingListenerTest.java | 30 +- .../newway/model/InvoiceWrapperTest.java | 17 - .../newway/model/PaymentWrapperTest.java | 17 - .../service/CashFlowWrapperHandlerTest.java | 130 + .../service/InvoiceBatchServiceTest.java | 66 - .../service/InvoiceWrapperServiceTest.java | 101 +- .../newway/service/InvoicingServiceTest.java | 159 +- .../service/PartyManagementServiceTest.java | 2 +- .../service/PaymentBatchServiceTest.java | 81 - .../service/PaymentSquashServiceTest.java | 176 -- .../service/PaymentWrapperServiceTest.java | 134 + .../newway/service/RateServiceTests.java | 12 +- .../RecurrentPaymentToolServiceTest.java | 7 +- .../vality/newway/util/CashFlowUtilTest.java | 1 + ...ilTest.java => ContractorFactoryTest.java} | 11 +- .../dev/vality/newway/utils/JdbcUtil.java | 42 + .../dev/vality/newway/utils/MockUtils.java | 39 +- .../newway/utils/PaymentWrapperTestUtil.java | 77 + 295 files changed, 5585 insertions(+), 4635 deletions(-) create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/iface/CashFlowLinkDao.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/iface/InvoiceStatusInfoDao.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentAdditionalInfoDao.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentFeeDao.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentPayerInfoDao.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentRecurrentInfoDao.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentRiskDataDao.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentRouteDao.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentStatusInfoDao.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/impl/CashFlowLinkDaoImpl.java rename src/main/java/dev/vality/newway/dao/invoicing/impl/{PaymentIdsGeneratorDaoImpl.java => CashFlowLinkIdsGeneratorDaoImpl.java} (84%) delete mode 100644 src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceIdsGeneratorDaoImpl.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceStatusInfoDaoImpl.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentAdditionalInfoDaoImpl.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentFeeDaoImpl.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentPayerInfoDaoImpl.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentRecurrentInfoDaoImpl.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentRiskDataDaoImpl.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentRouteDaoImpl.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentStatusInfoDaoImpl.java create mode 100644 src/main/java/dev/vality/newway/factory/cash/flow/CashFlowFactory.java create mode 100644 src/main/java/dev/vality/newway/factory/cash/flow/CashFlowLinkFactory.java rename src/main/java/dev/vality/newway/{util/ContractorUtil.java => factory/contractor/ContractorFactory.java} (91%) create mode 100644 src/main/java/dev/vality/newway/factory/invoice/payment/InvoicePaymentEventIdHolderFactory.java create mode 100644 src/main/java/dev/vality/newway/factory/invoice/payment/PaymentFeeFactory.java create mode 100644 src/main/java/dev/vality/newway/factory/invoice/payment/PaymentRouteFactory.java create mode 100644 src/main/java/dev/vality/newway/factory/invoice/payment/PaymentStatusInfoFactory.java rename src/main/java/dev/vality/newway/factory/{ => machine/event}/AdjustmentMachineEventCopyFactoryImpl.java (95%) rename src/main/java/dev/vality/newway/factory/{ => machine/event}/ChallengeMachineEventCopyFactoryImpl.java (96%) rename src/main/java/dev/vality/newway/factory/{ => machine/event}/ChargebackMachineEventCopyFactoryImpl.java (95%) rename src/main/java/dev/vality/newway/factory/{ => machine/event}/DepositAjustmentMachineEventCopyFactoryImpl.java (96%) rename src/main/java/dev/vality/newway/factory/{ => machine/event}/DepositMachineEventCopyFactoryImpl.java (96%) rename src/main/java/dev/vality/newway/factory/{ => machine/event}/DepositRevertMachineEventCopyFactoryImpl.java (96%) rename src/main/java/dev/vality/newway/factory/{ => machine/event}/DestinationMachineEventCopyFactoryImpl.java (96%) rename src/main/java/dev/vality/newway/factory/{ => machine/event}/IdentityMachineEventCopyFactoryImpl.java (95%) rename src/main/java/dev/vality/newway/factory/{ => machine/event}/MachineEventCopyFactory.java (84%) rename src/main/java/dev/vality/newway/factory/{ => machine/event}/PartyMachineEventCopyFactoryImpl.java (95%) rename src/main/java/dev/vality/newway/factory/{ => machine/event}/RecurrentPaymentToolCopyFactoryImpl.java (96%) rename src/main/java/dev/vality/newway/factory/{ => machine/event}/RefundMachineEventCopyFactoryImpl.java (95%) rename src/main/java/dev/vality/newway/factory/{ => machine/event}/SourceMachineEventCopyFactoryImpl.java (95%) rename src/main/java/dev/vality/newway/factory/{ => machine/event}/WalletMachineEventCopyFactoryImpl.java (95%) rename src/main/java/dev/vality/newway/factory/{ => machine/event}/WithdrawalMachineEventCopyFactoryImpl.java (96%) rename src/main/java/dev/vality/newway/factory/{ => machine/event}/WithdrawalSessionMachineEventCopyFactoryImpl.java (96%) delete mode 100644 src/main/java/dev/vality/newway/handler/event/stock/LocalStorage.java create mode 100644 src/main/java/dev/vality/newway/handler/wrapper/WrapperHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/wrapper/invoice/InvoiceCartWrapperHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/wrapper/invoice/InvoiceStatusInfoWrapperHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/wrapper/invoice/InvoiceWrapperHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/wrapper/payment/CashFlowWrapperHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentAdditionalInfoWrapperHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentFeeWrapperHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentPayerInfoWrapperHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRecurrentInfoWrapperHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRiskDataWrapperHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRouteWrapperHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentStatusInfoWrapperHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentWrapperHandler.java delete mode 100644 src/main/java/dev/vality/newway/mapper/AbstractInvoicingMapper.java delete mode 100644 src/main/java/dev/vality/newway/mapper/invoice/AbstractInvoicingInvoiceMapper.java delete mode 100644 src/main/java/dev/vality/newway/mapper/payment/AbstractInvoicingPaymentMapper.java rename src/main/java/dev/vality/newway/{util => model}/CashFlowType.java (95%) create mode 100644 src/main/java/dev/vality/newway/model/CashFlowWrapper.java create mode 100644 src/main/java/dev/vality/newway/model/InvoicePaymentEventIdHolder.java create mode 100644 src/main/java/dev/vality/newway/model/PartyShop.java delete mode 100644 src/main/java/dev/vality/newway/service/InvoiceBatchService.java create mode 100644 src/main/java/dev/vality/newway/service/PartyShopCacheService.java delete mode 100644 src/main/java/dev/vality/newway/service/PaymentBatchService.java delete mode 100644 src/main/java/dev/vality/newway/service/PaymentSquashService.java create mode 100644 src/main/java/dev/vality/newway/util/PaymentWrapperUtil.java delete mode 100644 src/main/resources/banner.png create mode 100644 src/main/resources/banner.txt delete mode 100644 src/main/resources/db/migration/V10__commissions.sql delete mode 100644 src/main/resources/db/migration/V11__add_recurrent_payer_type.sql delete mode 100644 src/main/resources/db/migration/V12__recurrents.sql delete mode 100644 src/main/resources/db/migration/V13__party_revision_to_refunds.sql delete mode 100644 src/main/resources/db/migration/V14__session_info_remove.sql delete mode 100644 src/main/resources/db/migration/V15__1.0.20_fistful_data.sql delete mode 100644 src/main/resources/db/migration/V16__1.0.23_add_deposit_destination_and_source_event_sink_data.sql delete mode 100644 src/main/resources/db/migration/V17__1.0.25_add_withdrawal_session.sql delete mode 100644 src/main/resources/db/migration/V18__1.0.26_add_rate.sql delete mode 100644 src/main/resources/db/migration/V19__1.0.27_payout_wallet.sql delete mode 100644 src/main/resources/db/migration/V20__1.0.28_fix_withdrawal_session.sql delete mode 100644 src/main/resources/db/migration/V21__1.0.33_remove_unused_columns_in_payout.sql delete mode 100644 src/main/resources/db/migration/V22__1.0.35_remove_revision_not_null_constraint.sql delete mode 100644 src/main/resources/db/migration/V23__1.0.36_add_wallet_payout_tool_info_type.sql delete mode 100644 src/main/resources/db/migration/V24__1.0.36_add_wallet_payout_tool_info_columns.sql delete mode 100644 src/main/resources/db/migration/V25__1.0.37_add_payout_type.sql delete mode 100644 src/main/resources/db/migration/V26__1.0.39_actualized_fistful_proto.sql delete mode 100644 src/main/resources/db/migration/V27__1.0.43_add_payment_method_type.sql delete mode 100644 src/main/resources/db/migration/V28__1.0.44_add_change_id_remove_event_id.sql delete mode 100644 src/main/resources/db/migration/V29__1.0.46_add_additional_payment_info.sql delete mode 100644 src/main/resources/db/migration/V2__cash_flow_aggregate_functions.sql delete mode 100644 src/main/resources/db/migration/V30__1.0.46_crypto_payment_tool_type.sql delete mode 100644 src/main/resources/db/migration/V31__1.0.46_crypto_wallet.sql delete mode 100644 src/main/resources/db/migration/V32__add_resource_type_in_withdrawal_session.sql delete mode 100644 src/main/resources/db/migration/V33__1.0.51_add_adjustment_status.sql delete mode 100644 src/main/resources/db/migration/V34__1.0.52_update_fistful_proto.sql delete mode 100644 src/main/resources/db/migration/V35__1.0.53_xrates_sequence_id.sql delete mode 100644 src/main/resources/db/migration/V36__add_capture_started_reason.sql delete mode 100644 src/main/resources/db/migration/V37__add_mobile_commerce_type.sql delete mode 100644 src/main/resources/db/migration/V38__add_payment_mobile_commerce.sql delete mode 100644 src/main/resources/db/migration/V39__add_payment_method_mobile.sql delete mode 100644 src/main/resources/db/migration/V3__party_revision.sql delete mode 100644 src/main/resources/db/migration/V40__1.0.56_batch.sql delete mode 100644 src/main/resources/db/migration/V41__1.0.58_recurrent_payment_tool.sql delete mode 100644 src/main/resources/db/migration/V42__1.0.60_external_id.sql delete mode 100644 src/main/resources/db/migration/V43__1.0.61_drop_not_null.sql delete mode 100644 src/main/resources/db/migration/V44__1.0.62_add_payment_bank_info_columns.sql delete mode 100644 src/main/resources/db/migration/V45__1.0.62_add_payment_system_in_rate.sql delete mode 100644 src/main/resources/db/migration/V46__1.0.64_withdrawal_provider.sql delete mode 100644 src/main/resources/db/migration/V47__1.0.65_add_payment_status_adjustment.sql delete mode 100644 src/main/resources/db/migration/V48__1.0.66_add_sequence_change_id.sql delete mode 100644 src/main/resources/db/migration/V49__1.0.67_remove_rec_paytool_event_id.sql delete mode 100644 src/main/resources/db/migration/V4__1.0.6_optimize_cash_flow_aggregate_functions.sql delete mode 100644 src/main/resources/db/migration/V50__1.0.68_add_revision.sql delete mode 100644 src/main/resources/db/migration/V51__cntrct_seq.sql delete mode 100644 src/main/resources/db/migration/V52__drop_index.sql delete mode 100644 src/main/resources/db/migration/V53__drop_fk_party.sql delete mode 100644 src/main/resources/db/migration/V54__add_ids_contr.sql delete mode 100644 src/main/resources/db/migration/V55__drop_cntrct_seq.sql delete mode 100644 src/main/resources/db/migration/V56__revision_tables.sql delete mode 100644 src/main/resources/db/migration/V57.1__add_chargeback.sql delete mode 100644 src/main/resources/db/migration/V57.2__add_chargeback.sql delete mode 100644 src/main/resources/db/migration/V58__drop_f_payment_system_rates.sql delete mode 100644 src/main/resources/db/migration/V59__revert_V58.sql delete mode 100644 src/main/resources/db/migration/V5__domain_objects.sql delete mode 100644 src/main/resources/db/migration/V60__add_adjustment_cashflow_amount.sql delete mode 100644 src/main/resources/db/migration/V61__drop_f_payment_system_rates.sql delete mode 100644 src/main/resources/db/migration/V62__new_withdrawal_provider_id.sql delete mode 100644 src/main/resources/db/migration/V63__optional_abs_account.sql delete mode 100644 src/main/resources/db/migration/V64__optional_terminal_json.sql delete mode 100644 src/main/resources/db/migration/V65__add_payer_cardholder_name.sql delete mode 100644 src/main/resources/db/migration/V66__unied_provider-n-ext-term.sql delete mode 100644 src/main/resources/db/migration/V67__add_terminal_provider_ref.sql delete mode 100644 src/main/resources/db/migration/V68__remove_not_null_contraint_terminal.sql delete mode 100644 src/main/resources/db/migration/V69__add_charged_back_payment_status.sql delete mode 100644 src/main/resources/db/migration/V6__1.0.10_swift_data.sql delete mode 100644 src/main/resources/db/migration/V71__add_payment_routing_rules_table.sql delete mode 100644 src/main/resources/db/migration/V72__change_type_for_routing_decisions_column.sql delete mode 100644 src/main/resources/db/migration/V73__rename_rule_id_and_add_version_column.sql delete mode 100644 src/main/resources/db/migration/V74__change_fileds_for_new_seq_scheme.sql delete mode 100644 src/main/resources/db/migration/V75__add_new_payment_systems.sql delete mode 100644 src/main/resources/db/migration/V76__add_uzcard_payment_system.sql delete mode 100644 src/main/resources/db/migration/V77__add_deposit_revert_type.sql delete mode 100644 src/main/resources/db/migration/V78__add_deposit_adjustment_type.sql delete mode 100644 src/main/resources/db/migration/V79__revert_and_adjustment_deposit_tables.sql delete mode 100644 src/main/resources/db/migration/V7__domain_objects.sql delete mode 100644 src/main/resources/db/migration/V80__change_payment_method_ids.sql delete mode 100644 src/main/resources/db/migration/V81__fix_deposit_adjustment_and_revert.sql delete mode 100644 src/main/resources/db/migration/V82__add_crypto_currency_deprecated_type.sql delete mode 100644 src/main/resources/db/migration/V83__change_mobile_operator_type.sql delete mode 100644 src/main/resources/db/migration/V84__add_country_and_trade_bloc.sql delete mode 100644 src/main/resources/db/migration/V85__fix_not_req_field.sql delete mode 100644 src/main/resources/db/migration/V86__destination_type_digital_wallet.sql delete mode 100644 src/main/resources/db/migration/V87__destination_digital_wallet.sql delete mode 100644 src/main/resources/db/migration/V88__new_payouts.sql delete mode 100644 src/main/resources/db/migration/V89__add_payout_tool_info_type.sql delete mode 100644 src/main/resources/db/migration/V8__session_change.sql delete mode 100644 src/main/resources/db/migration/V90__drop_non_null_provider_json_field.sql delete mode 100644 src/main/resources/db/migration/V91__drop_cls_field_identity.sql delete mode 100644 src/main/resources/db/migration/V92__add_schedlock.sql delete mode 100644 src/main/resources/db/migration/V93__add_adjustmen_diffs.sql delete mode 100644 src/main/resources/db/migration/V94__add_generic_type.sql delete mode 100644 src/main/resources/db/migration/V9__truncate_party_mngmnt_for_contractors.sql delete mode 100644 src/test/java/dev/vality/newway/model/InvoiceWrapperTest.java delete mode 100644 src/test/java/dev/vality/newway/model/PaymentWrapperTest.java create mode 100644 src/test/java/dev/vality/newway/service/CashFlowWrapperHandlerTest.java delete mode 100644 src/test/java/dev/vality/newway/service/InvoiceBatchServiceTest.java delete mode 100644 src/test/java/dev/vality/newway/service/PaymentBatchServiceTest.java delete mode 100644 src/test/java/dev/vality/newway/service/PaymentSquashServiceTest.java create mode 100644 src/test/java/dev/vality/newway/service/PaymentWrapperServiceTest.java rename src/test/java/dev/vality/newway/util/{ContractorUtilTest.java => ContractorFactoryTest.java} (83%) create mode 100644 src/test/java/dev/vality/newway/utils/JdbcUtil.java create mode 100644 src/test/java/dev/vality/newway/utils/PaymentWrapperTestUtil.java diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a6547029..3b310f08 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,7 +3,7 @@ name: Maven Build Artifact on: pull_request: branches: - - '*' + - '**' jobs: build: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6a394b04..5a405cb6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -5,6 +5,7 @@ on: branches: - 'master' - 'main' + - 'epic/**' env: REGISTRY: ghcr.io diff --git a/pom.xml b/pom.xml index d3476ebd..0168d8d3 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ postgres postgres newway - nw + dw jdbc:postgresql://localhost:5432/newway 5432 ./src/main/resources/checkstyle/checkstyle-suppressions.xml diff --git a/src/main/java/dev/vality/newway/config/ApplicationConfig.java b/src/main/java/dev/vality/newway/config/ApplicationConfig.java index 3270b24c..9f308c5b 100644 --- a/src/main/java/dev/vality/newway/config/ApplicationConfig.java +++ b/src/main/java/dev/vality/newway/config/ApplicationConfig.java @@ -1,7 +1,7 @@ package dev.vality.newway.config; import dev.vality.damsel.domain_config.RepositorySrv; -import dev.vality.newway.domain.Nw; +import dev.vality.newway.domain.Dw; import dev.vality.woody.thrift.impl.http.THSpawnClientBuilder; import org.jooq.Schema; import org.springframework.beans.factory.annotation.Value; @@ -25,6 +25,6 @@ public RepositorySrv.Iface dominantClient(@Value("${dmt.url}") Resource resource @Bean public Schema schema() { - return Nw.NW; + return Dw.DW; } } diff --git a/src/main/java/dev/vality/newway/config/CacheConfig.java b/src/main/java/dev/vality/newway/config/CacheConfig.java index 34dce07b..db105f4b 100644 --- a/src/main/java/dev/vality/newway/config/CacheConfig.java +++ b/src/main/java/dev/vality/newway/config/CacheConfig.java @@ -2,23 +2,23 @@ import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; -import dev.vality.newway.model.InvoiceWrapper; -import dev.vality.newway.model.InvoicingKey; -import dev.vality.newway.model.PaymentWrapper; +import dev.vality.newway.model.PartyShop; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.time.Duration; + @Configuration public class CacheConfig { @Bean - public Cache invoiceDataCache(@Value("${cache.invoice.size}") int cacheSize) { - return Caffeine.newBuilder().maximumSize(cacheSize).build(); + public Cache partyShopDataCache(@Value("${cache.party-shop.size}") int cacheSize, + @Value("${cache.party-shop.expire.after.sec}") long expireAfter) { + return Caffeine.newBuilder() + .maximumSize(cacheSize) + .expireAfterWrite(Duration.ofSeconds(expireAfter)) + .build(); } - @Bean - public Cache paymentDataCache(@Value("${cache.payment.size}") int cacheSize) { - return Caffeine.newBuilder().maximumSize(cacheSize).build(); - } } diff --git a/src/main/java/dev/vality/newway/config/SchedulerConfig.java b/src/main/java/dev/vality/newway/config/SchedulerConfig.java index 4282a496..38d90a2e 100644 --- a/src/main/java/dev/vality/newway/config/SchedulerConfig.java +++ b/src/main/java/dev/vality/newway/config/SchedulerConfig.java @@ -18,7 +18,7 @@ @EnableSchedulerLock(defaultLockAtMostFor = "PT5M") public class SchedulerConfig { - public static final String TABLE_NAME = "nw.shedlock"; + public static final String TABLE_NAME = "dw.shedlock"; @Bean public DominantPoller dominantPoller(RepositorySrv.Iface dominantClient, diff --git a/src/main/java/dev/vality/newway/dao/invoicing/iface/CashFlowLinkDao.java b/src/main/java/dev/vality/newway/dao/invoicing/iface/CashFlowLinkDao.java new file mode 100644 index 00000000..58c3992f --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/iface/CashFlowLinkDao.java @@ -0,0 +1,22 @@ +package dev.vality.newway.dao.invoicing.iface; + +import dev.vality.dao.GenericDao; +import dev.vality.newway.domain.tables.pojos.CashFlowLink; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.model.InvoicePaymentEventIdHolder; +import dev.vality.newway.model.InvoicingKey; + +import java.util.List; +import java.util.Set; + +public interface CashFlowLinkDao extends GenericDao { + + void saveBatch(List links) throws DaoException; + + CashFlowLink get(String invoiceId, String paymentId); + + void switchCurrent(Set keys) throws DaoException; + + Set getExistingEvents(List links); + +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/iface/InvoiceCartDao.java b/src/main/java/dev/vality/newway/dao/invoicing/iface/InvoiceCartDao.java index dc6773ea..b284033c 100644 --- a/src/main/java/dev/vality/newway/dao/invoicing/iface/InvoiceCartDao.java +++ b/src/main/java/dev/vality/newway/dao/invoicing/iface/InvoiceCartDao.java @@ -5,11 +5,14 @@ import dev.vality.newway.exception.DaoException; import java.util.List; +import java.util.Set; public interface InvoiceCartDao extends GenericDao { void save(List invoiceCartList) throws DaoException; - List getByInvId(Long invId) throws DaoException; + List getByInvoiceId(String invoiceId) throws DaoException; + + Set getExistingInvoiceIds(Set invoiceIds) throws DaoException; } diff --git a/src/main/java/dev/vality/newway/dao/invoicing/iface/InvoiceDao.java b/src/main/java/dev/vality/newway/dao/invoicing/iface/InvoiceDao.java index 7813731b..5124af9b 100644 --- a/src/main/java/dev/vality/newway/dao/invoicing/iface/InvoiceDao.java +++ b/src/main/java/dev/vality/newway/dao/invoicing/iface/InvoiceDao.java @@ -3,9 +3,7 @@ import dev.vality.dao.GenericDao; import dev.vality.newway.domain.tables.pojos.Invoice; import dev.vality.newway.exception.DaoException; -import dev.vality.newway.model.InvoicingKey; -import java.util.Collection; import java.util.List; public interface InvoiceDao extends GenericDao { @@ -14,5 +12,4 @@ public interface InvoiceDao extends GenericDao { Invoice get(String invoiceId) throws DaoException; - void switchCurrent(Collection invoicesSwitchIds) throws DaoException; } \ No newline at end of file diff --git a/src/main/java/dev/vality/newway/dao/invoicing/iface/InvoiceStatusInfoDao.java b/src/main/java/dev/vality/newway/dao/invoicing/iface/InvoiceStatusInfoDao.java new file mode 100644 index 00000000..bab72c01 --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/iface/InvoiceStatusInfoDao.java @@ -0,0 +1,18 @@ +package dev.vality.newway.dao.invoicing.iface; + +import dev.vality.dao.GenericDao; +import dev.vality.newway.domain.tables.pojos.InvoiceStatusInfo; +import dev.vality.newway.exception.DaoException; + +import java.util.List; +import java.util.Set; + +public interface InvoiceStatusInfoDao extends GenericDao { + + void saveBatch(List statuses) throws DaoException; + + InvoiceStatusInfo get(String invoiceId); + + void switchCurrent(Set invoiceIds) throws DaoException; + +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentAdditionalInfoDao.java b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentAdditionalInfoDao.java new file mode 100644 index 00000000..bebf4fd8 --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentAdditionalInfoDao.java @@ -0,0 +1,19 @@ +package dev.vality.newway.dao.invoicing.iface; + +import dev.vality.dao.GenericDao; +import dev.vality.newway.domain.tables.pojos.PaymentAdditionalInfo; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.model.InvoicingKey; + +import java.util.List; +import java.util.Set; + +public interface PaymentAdditionalInfoDao extends GenericDao { + + void saveBatch(List paymentAdditionalInfos) throws DaoException; + + PaymentAdditionalInfo get(String invoiceId, String paymentId) throws DaoException; + + void switchCurrent(Set invoicesSwitchIds) throws DaoException; + +} \ No newline at end of file diff --git a/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentDao.java b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentDao.java index 076c54ca..ee94ade6 100644 --- a/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentDao.java +++ b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentDao.java @@ -3,18 +3,13 @@ import dev.vality.dao.GenericDao; import dev.vality.newway.domain.tables.pojos.Payment; import dev.vality.newway.exception.DaoException; -import dev.vality.newway.model.InvoicingKey; -import java.util.Collection; import java.util.List; public interface PaymentDao extends GenericDao { void saveBatch(List payments) throws DaoException; - void updateBatch(List records) throws DaoException; - Payment get(String invoiceId, String paymentId) throws DaoException; - void switchCurrent(Collection invoicesSwitchIds) throws DaoException; } \ No newline at end of file diff --git a/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentFeeDao.java b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentFeeDao.java new file mode 100644 index 00000000..90ed3359 --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentFeeDao.java @@ -0,0 +1,19 @@ +package dev.vality.newway.dao.invoicing.iface; + +import dev.vality.dao.GenericDao; +import dev.vality.newway.domain.tables.pojos.PaymentFee; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.model.InvoicingKey; + +import java.util.List; +import java.util.Set; + +public interface PaymentFeeDao extends GenericDao { + + void saveBatch(List paymentFees) throws DaoException; + + PaymentFee get(String invoiceId, String paymentId) throws DaoException; + + void switchCurrent(Set invoicingKeys) throws DaoException; + +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentPayerInfoDao.java b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentPayerInfoDao.java new file mode 100644 index 00000000..821817cb --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentPayerInfoDao.java @@ -0,0 +1,16 @@ +package dev.vality.newway.dao.invoicing.iface; + +import dev.vality.dao.GenericDao; + +import dev.vality.newway.domain.tables.pojos.PaymentPayerInfo; +import dev.vality.newway.exception.DaoException; + +import java.util.List; + +public interface PaymentPayerInfoDao extends GenericDao { + + void saveBatch(List payerInfos) throws DaoException; + + PaymentPayerInfo get(String invoiceId, String paymentId) throws DaoException; + +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentRecurrentInfoDao.java b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentRecurrentInfoDao.java new file mode 100644 index 00000000..c1a4490b --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentRecurrentInfoDao.java @@ -0,0 +1,19 @@ +package dev.vality.newway.dao.invoicing.iface; + +import dev.vality.dao.GenericDao; +import dev.vality.newway.domain.tables.pojos.PaymentRecurrentInfo; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.model.InvoicingKey; + +import java.util.List; +import java.util.Set; + +public interface PaymentRecurrentInfoDao extends GenericDao { + + void saveBatch(List paymentRecurrentInfos) throws DaoException; + + PaymentRecurrentInfo get(String invoiceId, String paymentId) throws DaoException; + + void switchCurrent(Set invoicesSwitchIds) throws DaoException; + +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentRiskDataDao.java b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentRiskDataDao.java new file mode 100644 index 00000000..8ab00f94 --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentRiskDataDao.java @@ -0,0 +1,19 @@ +package dev.vality.newway.dao.invoicing.iface; + +import dev.vality.dao.GenericDao; +import dev.vality.newway.domain.tables.pojos.PaymentRiskData; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.model.InvoicingKey; + +import java.util.List; +import java.util.Set; + +public interface PaymentRiskDataDao extends GenericDao { + + void saveBatch(List paymentRiskDataList) throws DaoException; + + PaymentRiskData get(String invoiceId, String paymentId) throws DaoException; + + void switchCurrent(Set invoicingKeys) throws DaoException; + +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentRouteDao.java b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentRouteDao.java new file mode 100644 index 00000000..447db170 --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentRouteDao.java @@ -0,0 +1,19 @@ +package dev.vality.newway.dao.invoicing.iface; + +import dev.vality.dao.GenericDao; +import dev.vality.newway.domain.tables.pojos.PaymentRoute; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.model.InvoicingKey; + +import java.util.List; +import java.util.Set; + +public interface PaymentRouteDao extends GenericDao { + + void saveBatch(List paymentRoutes) throws DaoException; + + PaymentRoute get(String invoiceId, String paymentId) throws DaoException; + + void switchCurrent(Set invoicingKeys) throws DaoException; + +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentStatusInfoDao.java b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentStatusInfoDao.java new file mode 100644 index 00000000..3315389c --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentStatusInfoDao.java @@ -0,0 +1,18 @@ +package dev.vality.newway.dao.invoicing.iface; + +import dev.vality.dao.GenericDao; +import dev.vality.newway.domain.tables.pojos.PaymentStatusInfo; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.model.InvoicingKey; + +import java.util.List; +import java.util.Set; + +public interface PaymentStatusInfoDao extends GenericDao { + + void saveBatch(List paymentStatusInfos) throws DaoException; + + PaymentStatusInfo get(String invoiceId, String paymentId) throws DaoException; + + void switchCurrent(Set invoicesSwitchIds) throws DaoException; +} \ No newline at end of file diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/CashFlowLinkDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/CashFlowLinkDaoImpl.java new file mode 100644 index 00000000..1fcc99c4 --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/CashFlowLinkDaoImpl.java @@ -0,0 +1,124 @@ +package dev.vality.newway.dao.invoicing.impl; + +import dev.vality.dao.impl.AbstractGenericDao; +import dev.vality.mapper.RecordRowMapper; +import dev.vality.newway.dao.invoicing.iface.CashFlowLinkDao; +import dev.vality.newway.domain.tables.pojos.CashFlowLink; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.exception.NotFoundException; +import dev.vality.newway.model.InvoicePaymentEventIdHolder; +import dev.vality.newway.model.InvoicingKey; +import org.jooq.Field; +import org.jooq.Query; +import org.jooq.impl.DSL; +import org.springframework.jdbc.core.DataClassRowMapper; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.*; +import java.util.stream.Collectors; + +import static dev.vality.newway.domain.Tables.CASH_FLOW_LINK; + +@Component +public class CashFlowLinkDaoImpl extends AbstractGenericDao implements CashFlowLinkDao { + + private final RowMapper rowMapper; + private final RowMapper invoicePaymentEventIdRowMapper; + + private static final Field[] INVOICE_PAYMENT_EVENT_ID_HOLDER_FIELDS = new Field[]{ + CASH_FLOW_LINK.INVOICE_ID, + CASH_FLOW_LINK.PAYMENT_ID, + CASH_FLOW_LINK.CHANGE_ID, + CASH_FLOW_LINK.SEQUENCE_ID + }; + + public CashFlowLinkDaoImpl(DataSource dataSource) { + super(dataSource); + rowMapper = new RecordRowMapper<>(CASH_FLOW_LINK, CashFlowLink.class); + invoicePaymentEventIdRowMapper = new DataClassRowMapper<>(InvoicePaymentEventIdHolder.class); + } + + @Override + public void saveBatch(List links) throws DaoException { + batchExecute(links.stream() + .map(status -> getDslContext().newRecord(CASH_FLOW_LINK, status)) + .map(invoiceStatusInfoRecord -> getDslContext().insertInto(CASH_FLOW_LINK) + .set(invoiceStatusInfoRecord)) + .collect(Collectors.toList()) + ); + } + + @Override + public CashFlowLink get(String invoiceId, String paymentId) { + Query query = getDslContext().selectFrom(CASH_FLOW_LINK) + .where(CASH_FLOW_LINK.INVOICE_ID.eq(invoiceId) + .and(CASH_FLOW_LINK.PAYMENT_ID.eq(paymentId)) + .and(CASH_FLOW_LINK.CURRENT)); + return Optional.ofNullable(fetchOne(query, rowMapper)) + .orElseThrow(() -> new NotFoundException( + String.format("CashFlowLink not found, invoiceId='%s', paymentId='%s'", invoiceId, paymentId))); + } + + @Override + public void switchCurrent(Set keys) throws DaoException { + keys.forEach(key -> { + setOldCashFlowLinkNotCurrent(key); + setLatestCashFlowLinkCurrent(key); + }); + } + + @Override + public Set getExistingEvents(List links) { + Set invoiceIds = new HashSet<>(); + Set paymentIds = new HashSet<>(); + Set changeIds = new HashSet<>(); + Set sequenceIds = new HashSet<>(); + Set concatenatedIds = new HashSet<>(); + links.forEach(link -> { + invoiceIds.add(link.getInvoiceId()); + paymentIds.add(link.getPaymentId()); + changeIds.add(link.getChangeId()); + sequenceIds.add(link.getSequenceId()); + concatenatedIds.add(link.getInvoiceId() + link.getPaymentId() + link.getChangeId() + link.getSequenceId()); + }); + + // we have to use concatenated ids otherwise there is small probability of collision. + // some non-processed events might fall under "invoice_id/payment_id/change_id/sequence_id in()" conditiona. + // e.g. we will receive several cash_flow_change events within one batch + // and there will be overlap in change ids. + // concat() is used as last step so there is minimal operation overhead. + Query query = getDslContext() + .select(INVOICE_PAYMENT_EVENT_ID_HOLDER_FIELDS) + .from(CASH_FLOW_LINK) + .where(CASH_FLOW_LINK.INVOICE_ID.in(invoiceIds)) + .and(CASH_FLOW_LINK.PAYMENT_ID.in(paymentIds)) + .and(CASH_FLOW_LINK.CHANGE_ID.in(changeIds)) + .and(CASH_FLOW_LINK.SEQUENCE_ID.in(sequenceIds)) + .and(DSL.concat(INVOICE_PAYMENT_EVENT_ID_HOLDER_FIELDS).in(concatenatedIds)); + + return new HashSet<>(fetch(query, invoicePaymentEventIdRowMapper)); + } + + private void setOldCashFlowLinkNotCurrent(InvoicingKey key) { + execute(getDslContext().update(CASH_FLOW_LINK) + .set(CASH_FLOW_LINK.CURRENT, false) + .where(CASH_FLOW_LINK.INVOICE_ID.eq(key.getInvoiceId()) + .and(CASH_FLOW_LINK.PAYMENT_ID.eq(key.getPaymentId())) + .and(CASH_FLOW_LINK.CURRENT)) + ); + } + + private void setLatestCashFlowLinkCurrent(InvoicingKey key) { + execute(getDslContext().update(CASH_FLOW_LINK) + .set(CASH_FLOW_LINK.CURRENT, true) + .where(CASH_FLOW_LINK.ID.eq( + DSL.select(DSL.max(CASH_FLOW_LINK.ID)) + .from(CASH_FLOW_LINK) + .where(CASH_FLOW_LINK.INVOICE_ID.eq(key.getInvoiceId()) + .and(CASH_FLOW_LINK.PAYMENT_ID.eq(key.getPaymentId()))) + )) + ); + } +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentIdsGeneratorDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/CashFlowLinkIdsGeneratorDaoImpl.java similarity index 84% rename from src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentIdsGeneratorDaoImpl.java rename to src/main/java/dev/vality/newway/dao/invoicing/impl/CashFlowLinkIdsGeneratorDaoImpl.java index eb359bc7..018a964b 100644 --- a/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentIdsGeneratorDaoImpl.java +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/CashFlowLinkIdsGeneratorDaoImpl.java @@ -14,14 +14,14 @@ @Slf4j @Component @RequiredArgsConstructor -public class PaymentIdsGeneratorDaoImpl implements IdsGeneratorDao { +public class CashFlowLinkIdsGeneratorDaoImpl implements IdsGeneratorDao { private final NamedParameterJdbcTemplate jdbcTemplate; @Override public List get(int size) throws DaoException { try { - String sql = "select nextval('nw.pmnt_seq') from generate_series(1, :size)"; + String sql = "select nextval('dw.cash_flow_link_id_seq') from generate_series(1, :size)"; MapSqlParameterSource parameterSource = new MapSqlParameterSource().addValue("size", size); return jdbcTemplate.queryForList(sql, parameterSource, Long.class); } catch (NestedRuntimeException e) { diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceCartDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceCartDaoImpl.java index 6d734db4..ab285f42 100644 --- a/src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceCartDaoImpl.java +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceCartDaoImpl.java @@ -6,12 +6,16 @@ import dev.vality.newway.domain.tables.pojos.InvoiceCart; import dev.vality.newway.exception.DaoException; import org.jooq.Query; +import org.jooq.impl.DSL; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.SingleColumnRowMapper; import org.springframework.stereotype.Component; import javax.sql.DataSource; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import static dev.vality.newway.domain.tables.InvoiceCart.INVOICE_CART; @@ -37,9 +41,29 @@ public void save(List carts) throws DaoException { } @Override - public List getByInvId(Long invId) throws DaoException { + public List getByInvoiceId(String invoiceId) throws DaoException { Query query = getDslContext().selectFrom(INVOICE_CART) - .where(INVOICE_CART.INV_ID.eq(invId)); + .where(INVOICE_CART.INVOICE_ID.eq(invoiceId)); return fetch(query, invoiceCartRowMapper); } + + /** + * Invoice cart can be written only once when Invoice is created. + * Invoice cart cannot be changed, only way to change invoice cart is to cancel invoice and create new one. + * + * @param invoiceIds set of invoice ids to check for existence. + * @return List of invoice ids which haven't been saved already. + * @throws DaoException + */ + @Override + public Set getExistingInvoiceIds(Set invoiceIds) throws DaoException { + Query query = getDslContext() + .select(INVOICE_CART.INVOICE_ID) + .from(INVOICE_CART) + .where(INVOICE_CART.INVOICE_ID.in(invoiceIds)) + .groupBy(INVOICE_CART.INVOICE_ID) + .having(DSL.count(INVOICE_CART.ID).greaterThan(0)); + + return new HashSet<>(fetch(query, new SingleColumnRowMapper<>(String.class))); + } } diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceDaoImpl.java index 1490b4f8..99be3cca 100644 --- a/src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceDaoImpl.java +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceDaoImpl.java @@ -4,18 +4,16 @@ import dev.vality.mapper.RecordRowMapper; import dev.vality.newway.dao.invoicing.iface.InvoiceDao; import dev.vality.newway.domain.tables.pojos.Invoice; +import dev.vality.newway.domain.tables.records.InvoiceRecord; import dev.vality.newway.exception.DaoException; import dev.vality.newway.exception.NotFoundException; -import dev.vality.newway.model.InvoicingKey; import org.jooq.Query; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.stereotype.Component; import javax.sql.DataSource; import javax.validation.constraints.NotNull; -import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -37,11 +35,7 @@ public InvoiceDaoImpl(DataSource dataSource) { public void saveBatch(List invoices) throws DaoException { List queries = invoices.stream() .map(invoice -> getDslContext().newRecord(INVOICE, invoice)) - .map(invoiceRecord -> getDslContext().insertInto(INVOICE) - .set(invoiceRecord) - .onConflict(INVOICE.INVOICE_ID, INVOICE.SEQUENCE_ID, INVOICE.CHANGE_ID) - .doNothing() - ) + .map(this::prepareInsertQuery) .collect(Collectors.toList()); batchExecute(queries); } @@ -50,20 +44,21 @@ public void saveBatch(List invoices) throws DaoException { @Override public Invoice get(String invoiceId) throws DaoException { Query query = getDslContext().selectFrom(INVOICE) - .where(INVOICE.INVOICE_ID.eq(invoiceId).and(INVOICE.CURRENT)); + .where(INVOICE.INVOICE_ID.eq(invoiceId)); return Optional.ofNullable(fetchOne(query, invoiceRowMapper)) .orElseThrow( () -> new NotFoundException(String.format("Invoice not found, invoiceId='%s'", invoiceId))); } - @Override - public void switchCurrent(Collection invoicesSwitchIds) throws DaoException { - invoicesSwitchIds.forEach(ik -> - this.getNamedParameterJdbcTemplate() - .update("update nw.invoice set current = false " + - "where invoice_id =:invoice_id and current;" + - "update nw.invoice set current = true " + - "where id = (select max(id) from nw.invoice where invoice_id =:invoice_id);", - new MapSqlParameterSource("invoice_id", ik.getInvoiceId()))); + private Query prepareInsertQuery(InvoiceRecord invoiceRecord) { + return getDslContext().insertInto(INVOICE) + .set(invoiceRecord) + .onConflict( + INVOICE.INVOICE_ID, + INVOICE.SEQUENCE_ID, + INVOICE.CHANGE_ID + ) + .doNothing(); } + } diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceIdsGeneratorDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceIdsGeneratorDaoImpl.java deleted file mode 100644 index be6da94d..00000000 --- a/src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceIdsGeneratorDaoImpl.java +++ /dev/null @@ -1,29 +0,0 @@ -package dev.vality.newway.dao.invoicing.impl; - -import dev.vality.newway.dao.invoicing.iface.IdsGeneratorDao; -import dev.vality.newway.exception.DaoException; -import lombok.RequiredArgsConstructor; -import org.springframework.core.NestedRuntimeException; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; -import org.springframework.stereotype.Component; - -import java.util.List; - -@Component -@RequiredArgsConstructor -public class InvoiceIdsGeneratorDaoImpl implements IdsGeneratorDao { - - private final NamedParameterJdbcTemplate jdbcTemplate; - - @Override - public List get(int size) throws DaoException { - try { - String sql = "select nextval('nw.inv_seq') from generate_series(1, :size)"; - MapSqlParameterSource parameterSource = new MapSqlParameterSource().addValue("size", size); - return jdbcTemplate.queryForList(sql, parameterSource, Long.class); - } catch (NestedRuntimeException e) { - throw new DaoException(e); - } - } -} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceStatusInfoDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceStatusInfoDaoImpl.java new file mode 100644 index 00000000..cbc49a3b --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/InvoiceStatusInfoDaoImpl.java @@ -0,0 +1,87 @@ +package dev.vality.newway.dao.invoicing.impl; + +import dev.vality.dao.impl.AbstractGenericDao; +import dev.vality.mapper.RecordRowMapper; +import dev.vality.newway.dao.invoicing.iface.InvoiceStatusInfoDao; +import dev.vality.newway.domain.tables.pojos.InvoiceStatusInfo; +import dev.vality.newway.domain.tables.records.InvoiceStatusInfoRecord; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.exception.NotFoundException; +import org.jooq.Query; +import org.jooq.impl.DSL; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static dev.vality.newway.domain.Tables.*; + +@Component +public class InvoiceStatusInfoDaoImpl extends AbstractGenericDao implements InvoiceStatusInfoDao { + + private final RowMapper rowMapper; + + public InvoiceStatusInfoDaoImpl(DataSource dataSource) { + super(dataSource); + this.rowMapper = new RecordRowMapper<>(INVOICE_STATUS_INFO, InvoiceStatusInfo.class); + } + + @Override + public void saveBatch(List statuses) throws DaoException { + batchExecute(statuses.stream() + .map(status -> getDslContext().newRecord(INVOICE_STATUS_INFO, status)) + .map(this::prepareInsertQuery) + .collect(Collectors.toList()) + ); + } + + @Override + public InvoiceStatusInfo get(String invoiceId) { + Query query = getDslContext().selectFrom(INVOICE_STATUS_INFO) + .where(INVOICE_STATUS_INFO.INVOICE_ID.eq(invoiceId) + .and(INVOICE_STATUS_INFO.CURRENT)); + return Optional.ofNullable(fetchOne(query, rowMapper)).orElseThrow(() -> + new NotFoundException(String.format("InvoiceStatusInfo not found, invoiceId='%s'", invoiceId))); + } + + @Override + public void switchCurrent(Set invoiceIds) throws DaoException { + invoiceIds.forEach(invoiceId -> { + setOldStatusInfoNotCurrent(invoiceId); + setLatestStatusInfoCurrent(invoiceId); + }); + } + + private Query prepareInsertQuery(InvoiceStatusInfoRecord invoiceStatusInfoRecord) { + return getDslContext().insertInto(INVOICE_STATUS_INFO) + .set(invoiceStatusInfoRecord) + .onConflict( + INVOICE_STATUS_INFO.INVOICE_ID, + INVOICE_STATUS_INFO.SEQUENCE_ID, + INVOICE_STATUS_INFO.CHANGE_ID) + .doNothing(); + } + + private void setOldStatusInfoNotCurrent(String invoiceId) { + execute(getDslContext().update(INVOICE_STATUS_INFO) + .set(INVOICE_STATUS_INFO.CURRENT, false) + .where(INVOICE_STATUS_INFO.INVOICE_ID.eq(invoiceId) + .and(INVOICE_STATUS_INFO.CURRENT)) + ); + } + + private void setLatestStatusInfoCurrent(String invoiceId) { + execute(getDslContext().update(INVOICE_STATUS_INFO) + .set(INVOICE_STATUS_INFO.CURRENT, true) + .where(INVOICE_STATUS_INFO.ID.eq( + DSL.select(DSL.max(INVOICE_STATUS_INFO.ID)) + .from(INVOICE_STATUS_INFO) + .where(INVOICE_STATUS_INFO.INVOICE_ID.eq(invoiceId)) + )) + ); + } +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentAdditionalInfoDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentAdditionalInfoDaoImpl.java new file mode 100644 index 00000000..84da3e1e --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentAdditionalInfoDaoImpl.java @@ -0,0 +1,95 @@ +package dev.vality.newway.dao.invoicing.impl; + +import dev.vality.dao.impl.AbstractGenericDao; +import dev.vality.mapper.RecordRowMapper; +import dev.vality.newway.dao.invoicing.iface.PaymentAdditionalInfoDao; +import dev.vality.newway.domain.tables.pojos.PaymentAdditionalInfo; +import dev.vality.newway.domain.tables.records.PaymentAdditionalInfoRecord; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.exception.NotFoundException; +import dev.vality.newway.model.InvoicingKey; +import org.jooq.Query; +import org.jooq.impl.DSL; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static dev.vality.newway.domain.tables.PaymentAdditionalInfo.PAYMENT_ADDITIONAL_INFO; + +@Component +public class PaymentAdditionalInfoDaoImpl extends AbstractGenericDao implements PaymentAdditionalInfoDao { + + private final RowMapper rowMapper; + + public PaymentAdditionalInfoDaoImpl(DataSource dataSource) { + super(dataSource); + this.rowMapper = new RecordRowMapper<>(PAYMENT_ADDITIONAL_INFO, PaymentAdditionalInfo.class); + + } + + @Override + public void saveBatch(List paymentAdditionalInfos) throws DaoException { + List queries = paymentAdditionalInfos.stream() + .map(statusInfo -> getDslContext().newRecord(PAYMENT_ADDITIONAL_INFO, statusInfo)) + .map(this::prepareInsertQuery + ) + .collect(Collectors.toList()); + batchExecute(queries); + } + + @Override + public PaymentAdditionalInfo get(String invoiceId, String paymentId) throws DaoException { + Query query = getDslContext().selectFrom(PAYMENT_ADDITIONAL_INFO) + .where(PAYMENT_ADDITIONAL_INFO.INVOICE_ID.eq(invoiceId) + .and(PAYMENT_ADDITIONAL_INFO.PAYMENT_ID.eq(paymentId)) + .and(PAYMENT_ADDITIONAL_INFO.CURRENT)); + return Optional.ofNullable(fetchOne(query, rowMapper)).orElseThrow(() -> + new NotFoundException("PaymentAdditionalInfo not found, invoiceId=" + invoiceId + " paymentId=" + paymentId)); + } + + @Override + public void switchCurrent(Set invoicesSwitchIds) throws DaoException { + invoicesSwitchIds.forEach(key -> { + setOldAdditionalInfoNotCurrent(key); + setLatestAdditionalInfoCurrent(key); + }); + } + + private Query prepareInsertQuery(PaymentAdditionalInfoRecord record) { + return getDslContext().insertInto(PAYMENT_ADDITIONAL_INFO) + .set(record) + .onConflict( + PAYMENT_ADDITIONAL_INFO.INVOICE_ID, + PAYMENT_ADDITIONAL_INFO.PAYMENT_ID, + PAYMENT_ADDITIONAL_INFO.SEQUENCE_ID, + PAYMENT_ADDITIONAL_INFO.CHANGE_ID + ) + .doNothing(); + } + + private void setOldAdditionalInfoNotCurrent(InvoicingKey key) { + execute(getDslContext().update(PAYMENT_ADDITIONAL_INFO) + .set(PAYMENT_ADDITIONAL_INFO.CURRENT, false) + .where(PAYMENT_ADDITIONAL_INFO.INVOICE_ID.eq(key.getInvoiceId()) + .and(PAYMENT_ADDITIONAL_INFO.PAYMENT_ID.eq(key.getPaymentId())) + .and(PAYMENT_ADDITIONAL_INFO.CURRENT)) + ); + } + + private void setLatestAdditionalInfoCurrent(InvoicingKey key) { + execute(getDslContext().update(PAYMENT_ADDITIONAL_INFO) + .set(PAYMENT_ADDITIONAL_INFO.CURRENT, true) + .where(PAYMENT_ADDITIONAL_INFO.ID.eq( + DSL.select(DSL.max(PAYMENT_ADDITIONAL_INFO.ID)) + .from(PAYMENT_ADDITIONAL_INFO) + .where(PAYMENT_ADDITIONAL_INFO.INVOICE_ID.eq(key.getInvoiceId()) + .and(PAYMENT_ADDITIONAL_INFO.PAYMENT_ID.eq(key.getPaymentId()))) + )) + ); + } +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentDaoImpl.java index 7af1bbb7..2d6c0074 100644 --- a/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentDaoImpl.java +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentDaoImpl.java @@ -4,18 +4,16 @@ import dev.vality.mapper.RecordRowMapper; import dev.vality.newway.dao.invoicing.iface.PaymentDao; import dev.vality.newway.domain.tables.pojos.Payment; +import dev.vality.newway.domain.tables.records.PaymentRecord; import dev.vality.newway.exception.DaoException; import dev.vality.newway.exception.NotFoundException; -import dev.vality.newway.model.InvoicingKey; import org.jooq.Query; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.stereotype.Component; import javax.sql.DataSource; import javax.validation.constraints.NotNull; -import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -37,22 +35,7 @@ public PaymentDaoImpl(DataSource dataSource) { public void saveBatch(List payments) throws DaoException { List queries = payments.stream() .map(payment -> getDslContext().newRecord(PAYMENT, payment)) - .map(paymentRecord -> getDslContext().insertInto(PAYMENT) - .set(paymentRecord) - .onConflict(PAYMENT.INVOICE_ID, PAYMENT.SEQUENCE_ID, PAYMENT.CHANGE_ID) - .doNothing() - ) - .collect(Collectors.toList()); - batchExecute(queries); - } - - @Override - public void updateBatch(List payments) throws DaoException { - List queries = payments.stream() - .map(payment -> getDslContext().newRecord(PAYMENT, payment)) - .map(paymentRecord -> getDslContext().update(PAYMENT) - .set(paymentRecord) - .where(PAYMENT.ID.eq(paymentRecord.getId()))) + .map(this::prepareInsertQuery) .collect(Collectors.toList()); batchExecute(queries); } @@ -62,24 +45,22 @@ public void updateBatch(List payments) throws DaoException { public Payment get(String invoiceId, String paymentId) throws DaoException { Query query = getDslContext().selectFrom(PAYMENT) .where(PAYMENT.INVOICE_ID.eq(invoiceId) - .and(PAYMENT.PAYMENT_ID.eq(paymentId)) - .and(PAYMENT.CURRENT)); - + .and(PAYMENT.PAYMENT_ID.eq(paymentId))); return Optional.ofNullable(fetchOne(query, paymentRowMapper)) .orElseThrow(() -> new NotFoundException( String.format("Payment not found, invoiceId='%s', paymentId='%s'", invoiceId, paymentId))); } - @Override - public void switchCurrent(Collection paymentsSwitchIds) throws DaoException { - paymentsSwitchIds.forEach(ik -> - this.getNamedParameterJdbcTemplate() - .update("update nw.payment set current = false " + - "where invoice_id =:invoice_id and payment_id=:payment_id and current;" + - "update nw.payment set current = true " + - "where id = (select max(id) from nw.payment where invoice_id =:invoice_id " + - "and payment_id=:payment_id);", - new MapSqlParameterSource("invoice_id", ik.getInvoiceId()) - .addValue("payment_id", ik.getPaymentId()))); + private Query prepareInsertQuery(PaymentRecord paymentRecord) { + return getDslContext().insertInto(PAYMENT) + .set(paymentRecord) + .onConflict( + PAYMENT.INVOICE_ID, + PAYMENT.PAYMENT_ID, + PAYMENT.SEQUENCE_ID, + PAYMENT.CHANGE_ID + ) + .doNothing(); } + } diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentFeeDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentFeeDaoImpl.java new file mode 100644 index 00000000..fd5d0b16 --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentFeeDaoImpl.java @@ -0,0 +1,94 @@ +package dev.vality.newway.dao.invoicing.impl; + +import dev.vality.dao.impl.AbstractGenericDao; +import dev.vality.mapper.RecordRowMapper; +import dev.vality.newway.dao.invoicing.iface.PaymentFeeDao; +import dev.vality.newway.domain.tables.pojos.PaymentFee; +import dev.vality.newway.domain.tables.records.PaymentFeeRecord; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.exception.NotFoundException; +import dev.vality.newway.model.InvoicingKey; +import org.jooq.Query; +import org.jooq.impl.DSL; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static dev.vality.newway.domain.tables.PaymentFee.PAYMENT_FEE; + +@Component +public class PaymentFeeDaoImpl extends AbstractGenericDao implements PaymentFeeDao { + + private final RowMapper rowMapper; + + public PaymentFeeDaoImpl(DataSource dataSource) { + super(dataSource); + this.rowMapper = new RecordRowMapper<>(PAYMENT_FEE, PaymentFee.class); + } + + @Override + public void saveBatch(List paymentFees) throws DaoException { + List queries = paymentFees.stream() + .map(statusInfo -> getDslContext().newRecord(PAYMENT_FEE, statusInfo)) + .map(this::prepareInsertQuery) + .collect(Collectors.toList()); + batchExecute(queries); + } + + @Override + public PaymentFee get(String invoiceId, String paymentId) throws DaoException { + Query query = getDslContext().selectFrom(PAYMENT_FEE) + .where(PAYMENT_FEE.INVOICE_ID.eq(invoiceId) + .and(PAYMENT_FEE.PAYMENT_ID.eq(paymentId)) + .and(PAYMENT_FEE.CURRENT) + ); + return Optional.ofNullable(fetchOne(query, rowMapper)).orElseThrow(() -> + new NotFoundException("PaymentFee not found, invoiceId=" + invoiceId + " paymentId=" + paymentId)); + } + + @Override + public void switchCurrent(Set invoicingKeys) throws DaoException { + invoicingKeys.forEach(key -> { + setOldPaymentFeeNotCurrent(key); + setLatestPaymentFeeCurrent(key); + }); + } + + private Query prepareInsertQuery(PaymentFeeRecord record) { + return getDslContext().insertInto(PAYMENT_FEE) + .set(record) + .onConflict( + PAYMENT_FEE.INVOICE_ID, + PAYMENT_FEE.PAYMENT_ID, + PAYMENT_FEE.SEQUENCE_ID, + PAYMENT_FEE.CHANGE_ID + ) + .doNothing(); + } + + private void setOldPaymentFeeNotCurrent(InvoicingKey key) { + execute(getDslContext().update(PAYMENT_FEE) + .set(PAYMENT_FEE.CURRENT, false) + .where(PAYMENT_FEE.INVOICE_ID.eq(key.getInvoiceId()) + .and(PAYMENT_FEE.PAYMENT_ID.eq(key.getPaymentId())) + .and(PAYMENT_FEE.CURRENT)) + ); + } + + private void setLatestPaymentFeeCurrent(InvoicingKey key) { + execute(getDslContext().update(PAYMENT_FEE) + .set(PAYMENT_FEE.CURRENT, true) + .where(PAYMENT_FEE.ID.eq( + DSL.select(DSL.max(PAYMENT_FEE.ID)) + .from(PAYMENT_FEE) + .where(PAYMENT_FEE.INVOICE_ID.eq(key.getInvoiceId()) + .and(PAYMENT_FEE.PAYMENT_ID.eq(key.getPaymentId()))) + )) + ); + } +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentPayerInfoDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentPayerInfoDaoImpl.java new file mode 100644 index 00000000..71f65ed9 --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentPayerInfoDaoImpl.java @@ -0,0 +1,60 @@ +package dev.vality.newway.dao.invoicing.impl; + +import dev.vality.dao.impl.AbstractGenericDao; +import dev.vality.mapper.RecordRowMapper; +import dev.vality.newway.dao.invoicing.iface.PaymentPayerInfoDao; +import dev.vality.newway.domain.tables.pojos.PaymentPayerInfo; +import dev.vality.newway.domain.tables.records.PaymentPayerInfoRecord; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.exception.NotFoundException; +import org.jooq.Query; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static dev.vality.newway.domain.tables.PaymentPayerInfo.PAYMENT_PAYER_INFO; + +@Component +public class PaymentPayerInfoDaoImpl extends AbstractGenericDao implements PaymentPayerInfoDao { + + private final RowMapper rowMapper; + + public PaymentPayerInfoDaoImpl(DataSource dataSource) { + super(dataSource); + this.rowMapper = new RecordRowMapper<>(PAYMENT_PAYER_INFO, PaymentPayerInfo.class); + } + + @Override + public void saveBatch(List payerInfos) throws DaoException { + List queries = payerInfos.stream() + .map(statusInfo -> getDslContext().newRecord(PAYMENT_PAYER_INFO, statusInfo)) + .map(this::prepareInsertQuery) + .collect(Collectors.toList()); + batchExecute(queries); + } + + @Override + public PaymentPayerInfo get(String invoiceId, String paymentId) throws DaoException { + Query query = getDslContext().selectFrom(PAYMENT_PAYER_INFO) + .where(PAYMENT_PAYER_INFO.INVOICE_ID.eq(invoiceId) + .and(PAYMENT_PAYER_INFO.PAYMENT_ID.eq(paymentId))); + return Optional.ofNullable(fetchOne(query, rowMapper)).orElseThrow(() -> + new NotFoundException("PaymentPayerInfo not found, invoiceId=" + invoiceId + " paymentId=" + paymentId)); + } + + private Query prepareInsertQuery(PaymentPayerInfoRecord record) { + return getDslContext().insertInto(PAYMENT_PAYER_INFO) + .set(record) + .onConflict( + PAYMENT_PAYER_INFO.INVOICE_ID, + PAYMENT_PAYER_INFO.PAYMENT_ID, + PAYMENT_PAYER_INFO.SEQUENCE_ID, + PAYMENT_PAYER_INFO.CHANGE_ID + ) + .doNothing(); + } +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentRecurrentInfoDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentRecurrentInfoDaoImpl.java new file mode 100644 index 00000000..d6eed7c6 --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentRecurrentInfoDaoImpl.java @@ -0,0 +1,93 @@ +package dev.vality.newway.dao.invoicing.impl; + +import dev.vality.dao.impl.AbstractGenericDao; +import dev.vality.mapper.RecordRowMapper; +import dev.vality.newway.dao.invoicing.iface.PaymentRecurrentInfoDao; +import dev.vality.newway.domain.tables.pojos.PaymentRecurrentInfo; +import dev.vality.newway.domain.tables.records.PaymentRecurrentInfoRecord; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.exception.NotFoundException; +import dev.vality.newway.model.InvoicingKey; +import org.jooq.Query; +import org.jooq.impl.DSL; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static dev.vality.newway.domain.tables.PaymentRecurrentInfo.PAYMENT_RECURRENT_INFO; + +@Component +public class PaymentRecurrentInfoDaoImpl extends AbstractGenericDao implements PaymentRecurrentInfoDao { + + private final RowMapper rowMapper; + + public PaymentRecurrentInfoDaoImpl(DataSource dataSource) { + super(dataSource); + this.rowMapper = new RecordRowMapper<>(PAYMENT_RECURRENT_INFO, PaymentRecurrentInfo.class); + } + + @Override + public void saveBatch(List paymentRecurrentInfos) throws DaoException { + List queries = paymentRecurrentInfos.stream() + .map(statusInfo -> getDslContext().newRecord(PAYMENT_RECURRENT_INFO, statusInfo)) + .map(this::prepareInsertQuery) + .collect(Collectors.toList()); + batchExecute(queries); + } + + @Override + public PaymentRecurrentInfo get(String invoiceId, String paymentId) throws DaoException { + Query query = getDslContext().selectFrom(PAYMENT_RECURRENT_INFO) + .where(PAYMENT_RECURRENT_INFO.INVOICE_ID.eq(invoiceId) + .and(PAYMENT_RECURRENT_INFO.PAYMENT_ID.eq(paymentId)) + .and(PAYMENT_RECURRENT_INFO.CURRENT)); + return Optional.ofNullable(fetchOne(query, rowMapper)).orElseThrow(() -> + new NotFoundException("PaymentPayerInfo not found, invoiceId=" + invoiceId + " paymentId=" + paymentId)); + } + + @Override + public void switchCurrent(Set invoicesSwitchIds) throws DaoException { + invoicesSwitchIds.forEach(key -> { + setOldRecurrentInfoNotCurrent(key); + setLatestRecurrentInfoCurrent(key); + }); + } + + private Query prepareInsertQuery(PaymentRecurrentInfoRecord record) { + return getDslContext().insertInto(PAYMENT_RECURRENT_INFO) + .set(record) + .onConflict( + PAYMENT_RECURRENT_INFO.INVOICE_ID, + PAYMENT_RECURRENT_INFO.PAYMENT_ID, + PAYMENT_RECURRENT_INFO.SEQUENCE_ID, + PAYMENT_RECURRENT_INFO.CHANGE_ID + ) + .doNothing(); + } + + private void setOldRecurrentInfoNotCurrent(InvoicingKey key) { + execute(getDslContext().update(PAYMENT_RECURRENT_INFO) + .set(PAYMENT_RECURRENT_INFO.CURRENT, false) + .where(PAYMENT_RECURRENT_INFO.INVOICE_ID.eq(key.getInvoiceId()) + .and(PAYMENT_RECURRENT_INFO.PAYMENT_ID.eq(key.getPaymentId())) + .and(PAYMENT_RECURRENT_INFO.CURRENT)) + ); + } + + private void setLatestRecurrentInfoCurrent(InvoicingKey key) { + execute(getDslContext().update(PAYMENT_RECURRENT_INFO) + .set(PAYMENT_RECURRENT_INFO.CURRENT, true) + .where(PAYMENT_RECURRENT_INFO.ID.eq( + DSL.select(DSL.max(PAYMENT_RECURRENT_INFO.ID)) + .from(PAYMENT_RECURRENT_INFO) + .where(PAYMENT_RECURRENT_INFO.INVOICE_ID.eq(key.getInvoiceId()) + .and(PAYMENT_RECURRENT_INFO.PAYMENT_ID.eq(key.getPaymentId()))) + )) + ); + } +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentRiskDataDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentRiskDataDaoImpl.java new file mode 100644 index 00000000..3811b5ec --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentRiskDataDaoImpl.java @@ -0,0 +1,94 @@ +package dev.vality.newway.dao.invoicing.impl; + +import dev.vality.dao.impl.AbstractGenericDao; +import dev.vality.mapper.RecordRowMapper; +import dev.vality.newway.dao.invoicing.iface.PaymentRiskDataDao; +import dev.vality.newway.domain.tables.pojos.PaymentRiskData; +import dev.vality.newway.domain.tables.records.PaymentRiskDataRecord; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.exception.NotFoundException; +import dev.vality.newway.model.InvoicingKey; +import org.jooq.Query; +import org.jooq.impl.DSL; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static dev.vality.newway.domain.tables.PaymentRiskData.PAYMENT_RISK_DATA; + +@Component +public class PaymentRiskDataDaoImpl extends AbstractGenericDao implements PaymentRiskDataDao { + + private final RowMapper rowMapper; + + public PaymentRiskDataDaoImpl(DataSource dataSource) { + super(dataSource); + this.rowMapper = new RecordRowMapper<>(PAYMENT_RISK_DATA, PaymentRiskData.class); + } + + @Override + public void saveBatch(List paymentRiskDataList) throws DaoException { + List queries = paymentRiskDataList.stream() + .map(statusInfo -> getDslContext().newRecord(PAYMENT_RISK_DATA, statusInfo)) + .map(this::prepareInsertQuery) + .collect(Collectors.toList()); + batchExecute(queries); + } + + @Override + public PaymentRiskData get(String invoiceId, String paymentId) throws DaoException { + Query query = getDslContext().selectFrom(PAYMENT_RISK_DATA) + .where(PAYMENT_RISK_DATA.INVOICE_ID.eq(invoiceId) + .and(PAYMENT_RISK_DATA.PAYMENT_ID.eq(paymentId)) + .and(PAYMENT_RISK_DATA.CURRENT) + ); + return Optional.ofNullable(fetchOne(query, rowMapper)).orElseThrow(() -> + new NotFoundException("PaymentRiskData not found, invoiceId=" + invoiceId + " paymentId=" + paymentId)); + } + + @Override + public void switchCurrent(Set invoicingKeys) throws DaoException { + invoicingKeys.forEach(key -> { + setOldRiskDataNotCurrent(key); + setLatestRiskDataCurrent(key); + }); + } + + private Query prepareInsertQuery(PaymentRiskDataRecord record) { + return getDslContext().insertInto(PAYMENT_RISK_DATA) + .set(record) + .onConflict( + PAYMENT_RISK_DATA.INVOICE_ID, + PAYMENT_RISK_DATA.PAYMENT_ID, + PAYMENT_RISK_DATA.SEQUENCE_ID, + PAYMENT_RISK_DATA.CHANGE_ID + ) + .doNothing(); + } + + private void setOldRiskDataNotCurrent(InvoicingKey key) { + execute(getDslContext().update(PAYMENT_RISK_DATA) + .set(PAYMENT_RISK_DATA.CURRENT, false) + .where(PAYMENT_RISK_DATA.INVOICE_ID.eq(key.getInvoiceId()) + .and(PAYMENT_RISK_DATA.PAYMENT_ID.eq(key.getPaymentId())) + .and(PAYMENT_RISK_DATA.CURRENT)) + ); + } + + private void setLatestRiskDataCurrent(InvoicingKey key) { + execute(getDslContext().update(PAYMENT_RISK_DATA) + .set(PAYMENT_RISK_DATA.CURRENT, true) + .where(PAYMENT_RISK_DATA.ID.eq( + DSL.select(DSL.max(PAYMENT_RISK_DATA.ID)) + .from(PAYMENT_RISK_DATA) + .where(PAYMENT_RISK_DATA.INVOICE_ID.eq(key.getInvoiceId()) + .and(PAYMENT_RISK_DATA.PAYMENT_ID.eq(key.getPaymentId()))) + )) + ); + } +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentRouteDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentRouteDaoImpl.java new file mode 100644 index 00000000..cd182e4a --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentRouteDaoImpl.java @@ -0,0 +1,94 @@ +package dev.vality.newway.dao.invoicing.impl; + +import dev.vality.dao.impl.AbstractGenericDao; +import dev.vality.mapper.RecordRowMapper; +import dev.vality.newway.dao.invoicing.iface.PaymentRouteDao; +import dev.vality.newway.domain.tables.pojos.PaymentRoute; +import dev.vality.newway.domain.tables.records.PaymentRouteRecord; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.exception.NotFoundException; +import dev.vality.newway.model.InvoicingKey; +import org.jooq.Query; +import org.jooq.impl.DSL; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static dev.vality.newway.domain.tables.PaymentRoute.PAYMENT_ROUTE; + +@Component +public class PaymentRouteDaoImpl extends AbstractGenericDao implements PaymentRouteDao { + + private final RowMapper rowMapper; + + public PaymentRouteDaoImpl(DataSource dataSource) { + super(dataSource); + this.rowMapper = new RecordRowMapper<>(PAYMENT_ROUTE, PaymentRoute.class); + } + + @Override + public void saveBatch(List paymentRoutes) throws DaoException { + List queries = paymentRoutes.stream() + .map(statusInfo -> getDslContext().newRecord(PAYMENT_ROUTE, statusInfo)) + .map(this::prepareInsertQuery) + .collect(Collectors.toList()); + batchExecute(queries); + } + + @Override + public PaymentRoute get(String invoiceId, String paymentId) throws DaoException { + Query query = getDslContext().selectFrom(PAYMENT_ROUTE) + .where(PAYMENT_ROUTE.INVOICE_ID.eq(invoiceId) + .and(PAYMENT_ROUTE.PAYMENT_ID.eq(paymentId)) + .and(PAYMENT_ROUTE.CURRENT) + ); + return Optional.ofNullable(fetchOne(query, rowMapper)).orElseThrow(() -> + new NotFoundException("PaymentRoute not found, invoiceId=" + invoiceId + " paymentId=" + paymentId)); + } + + @Override + public void switchCurrent(Set invoicingKeys) throws DaoException { + invoicingKeys.forEach(key -> { + setOldRouteNotCurrent(key); + setLatestRouteCurrent(key); + }); + } + + private void setOldRouteNotCurrent(InvoicingKey key) { + execute(getDslContext().update(PAYMENT_ROUTE) + .set(PAYMENT_ROUTE.CURRENT, false) + .where(PAYMENT_ROUTE.INVOICE_ID.eq(key.getInvoiceId()) + .and(PAYMENT_ROUTE.PAYMENT_ID.eq(key.getPaymentId())) + .and(PAYMENT_ROUTE.CURRENT)) + ); + } + + private void setLatestRouteCurrent(InvoicingKey key) { + execute(getDslContext().update(PAYMENT_ROUTE) + .set(PAYMENT_ROUTE.CURRENT, true) + .where(PAYMENT_ROUTE.ID.eq( + DSL.select(DSL.max(PAYMENT_ROUTE.ID)) + .from(PAYMENT_ROUTE) + .where(PAYMENT_ROUTE.INVOICE_ID.eq(key.getInvoiceId()) + .and(PAYMENT_ROUTE.PAYMENT_ID.eq(key.getPaymentId()))) + )) + ); + } + + private Query prepareInsertQuery(PaymentRouteRecord record) { + return getDslContext().insertInto(PAYMENT_ROUTE) + .set(record) + .onConflict( + PAYMENT_ROUTE.INVOICE_ID, + PAYMENT_ROUTE.PAYMENT_ID, + PAYMENT_ROUTE.SEQUENCE_ID, + PAYMENT_ROUTE.CHANGE_ID + ) + .doNothing(); + } +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentStatusInfoDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentStatusInfoDaoImpl.java new file mode 100644 index 00000000..3d7f1745 --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentStatusInfoDaoImpl.java @@ -0,0 +1,95 @@ +package dev.vality.newway.dao.invoicing.impl; + +import dev.vality.dao.impl.AbstractGenericDao; +import dev.vality.mapper.RecordRowMapper; +import dev.vality.newway.dao.invoicing.iface.PaymentStatusInfoDao; +import dev.vality.newway.domain.tables.pojos.PaymentStatusInfo; +import dev.vality.newway.domain.tables.records.PaymentStatusInfoRecord; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.exception.NotFoundException; +import dev.vality.newway.model.InvoicingKey; +import org.jooq.Query; +import org.jooq.impl.DSL; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static dev.vality.newway.domain.tables.PaymentStatusInfo.PAYMENT_STATUS_INFO; + +@Component +public class PaymentStatusInfoDaoImpl extends AbstractGenericDao implements PaymentStatusInfoDao { + + private final RowMapper rowMapper; + + public PaymentStatusInfoDaoImpl(DataSource dataSource) { + super(dataSource); + this.rowMapper = new RecordRowMapper<>(PAYMENT_STATUS_INFO, PaymentStatusInfo.class); + } + + // TODO: try with resources? + + @Override + public void saveBatch(List paymentStatusInfos) throws DaoException { + List queries = paymentStatusInfos.stream() + .map(statusInfo -> getDslContext().newRecord(PAYMENT_STATUS_INFO, statusInfo)) + .map(this::prepareInsertQuery) + .collect(Collectors.toList()); + batchExecute(queries); + } + + @Override + public PaymentStatusInfo get(String invoiceId, String paymentId) throws DaoException { + Query query = getDslContext().selectFrom(PAYMENT_STATUS_INFO) + .where(PAYMENT_STATUS_INFO.INVOICE_ID.eq(invoiceId) + .and(PAYMENT_STATUS_INFO.PAYMENT_ID.eq(paymentId)) + .and(PAYMENT_STATUS_INFO.CURRENT)); + return Optional.ofNullable(fetchOne(query, rowMapper)).orElseThrow(() -> + new NotFoundException("PaymentStatusInfo not found, invoiceId=" + invoiceId + " paymentId=" + paymentId)); + } + + @Override + public void switchCurrent(Set invoicesSwitchIds) throws DaoException { + invoicesSwitchIds.forEach(key -> { + setOldStatusInfoNotCurrent(key); + setLatestStatusInfoCurrent(key); + }); + } + + private Query prepareInsertQuery(PaymentStatusInfoRecord record) { + return getDslContext().insertInto(PAYMENT_STATUS_INFO) + .set(record) + .onConflict( + PAYMENT_STATUS_INFO.INVOICE_ID, + PAYMENT_STATUS_INFO.PAYMENT_ID, + PAYMENT_STATUS_INFO.SEQUENCE_ID, + PAYMENT_STATUS_INFO.CHANGE_ID + ) + .doNothing(); + } + + private void setOldStatusInfoNotCurrent(InvoicingKey key) { + execute(getDslContext().update(PAYMENT_STATUS_INFO) + .set(PAYMENT_STATUS_INFO.CURRENT, false) + .where(PAYMENT_STATUS_INFO.INVOICE_ID.eq(key.getInvoiceId()) + .and(PAYMENT_STATUS_INFO.PAYMENT_ID.eq(key.getPaymentId())) + .and(PAYMENT_STATUS_INFO.CURRENT)) + ); + } + + private void setLatestStatusInfoCurrent(InvoicingKey key) { + execute(getDslContext().update(PAYMENT_STATUS_INFO) + .set(PAYMENT_STATUS_INFO.CURRENT, true) + .where(PAYMENT_STATUS_INFO.ID.eq( + DSL.select(DSL.max(PAYMENT_STATUS_INFO.ID)) + .from(PAYMENT_STATUS_INFO) + .where(PAYMENT_STATUS_INFO.INVOICE_ID.eq(key.getInvoiceId()) + .and(PAYMENT_STATUS_INFO.PAYMENT_ID.eq(key.getPaymentId()))) + )) + ); + } +} diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/RefundDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/RefundDaoImpl.java index 7ef47d39..44d9889c 100644 --- a/src/main/java/dev/vality/newway/dao/invoicing/impl/RefundDaoImpl.java +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/RefundDaoImpl.java @@ -64,15 +64,15 @@ public void updateCommissions(Long rfndId) throws DaoException { MapSqlParameterSource params = new MapSqlParameterSource("rfndId", rfndId).addValue("objType", PaymentChangeType.refund.name()); this.getNamedParameterJdbcTemplate().update( - "UPDATE nw.refund SET fee = (SELECT nw.get_refund_fee(nw.cash_flow.*) " + - "FROM nw.cash_flow WHERE obj_id = :rfndId " + - "AND obj_type = CAST(:objType as nw.payment_change_type)), " + - "provider_fee = (SELECT nw.get_refund_provider_fee(nw.cash_flow.*) " + - "FROM nw.cash_flow WHERE obj_id = :rfndId " + - "AND obj_type = CAST(:objType as nw.payment_change_type)), " + - "external_fee = (SELECT nw.get_refund_external_fee(nw.cash_flow.*) " + - "FROM nw.cash_flow WHERE obj_id = :rfndId " + - "AND obj_type = CAST(:objType as nw.payment_change_type)) " + + "UPDATE dw.refund SET fee = (SELECT dw.get_refund_fee(dw.cash_flow.*) " + + "FROM dw.cash_flow WHERE obj_id = :rfndId " + + "AND obj_type = CAST(:objType as dw.payment_change_type)), " + + "provider_fee = (SELECT dw.get_refund_provider_fee(dw.cash_flow.*) " + + "FROM dw.cash_flow WHERE obj_id = :rfndId " + + "AND obj_type = CAST(:objType as dw.payment_change_type)), " + + "external_fee = (SELECT dw.get_refund_external_fee(dw.cash_flow.*) " + + "FROM dw.cash_flow WHERE obj_id = :rfndId " + + "AND obj_type = CAST(:objType as dw.payment_change_type)) " + "WHERE id = :rfndId", params); } diff --git a/src/main/java/dev/vality/newway/dao/party/impl/RevisionDaoImpl.java b/src/main/java/dev/vality/newway/dao/party/impl/RevisionDaoImpl.java index 15332d70..e5ba6c0c 100644 --- a/src/main/java/dev/vality/newway/dao/party/impl/RevisionDaoImpl.java +++ b/src/main/java/dev/vality/newway/dao/party/impl/RevisionDaoImpl.java @@ -17,8 +17,8 @@ public RevisionDaoImpl(DataSource dataSource) { @Override public void saveShopsRevision(String partyId, long revision) throws DaoException { - getNamedParameterJdbcTemplate().update("insert into nw.shop_revision(obj_id, revision) " + - "select id, :revision from nw.shop where party_id = :party_id and current", + getNamedParameterJdbcTemplate().update("insert into dw.shop_revision(obj_id, revision) " + + "select id, :revision from dw.shop where party_id = :party_id and current", new MapSqlParameterSource() .addValue("party_id", partyId) .addValue("revision", revision)); @@ -26,8 +26,8 @@ public void saveShopsRevision(String partyId, long revision) throws DaoException @Override public void saveContractsRevision(String partyId, long revision) throws DaoException { - getNamedParameterJdbcTemplate().update("insert into nw.contract_revision(obj_id, revision) " + - "select id, :revision from nw.contract where party_id = :party_id and current", + getNamedParameterJdbcTemplate().update("insert into dw.contract_revision(obj_id, revision) " + + "select id, :revision from dw.contract where party_id = :party_id and current", new MapSqlParameterSource() .addValue("party_id", partyId) .addValue("revision", revision)); @@ -35,8 +35,8 @@ public void saveContractsRevision(String partyId, long revision) throws DaoExcep @Override public void saveContractorsRevision(String partyId, long revision) throws DaoException { - getNamedParameterJdbcTemplate().update("insert into nw.contractor_revision(obj_id, revision) " + - "select id, :revision from nw.contractor where party_id = :party_id and current", + getNamedParameterJdbcTemplate().update("insert into dw.contractor_revision(obj_id, revision) " + + "select id, :revision from dw.contractor where party_id = :party_id and current", new MapSqlParameterSource() .addValue("party_id", partyId) .addValue("revision", revision)); diff --git a/src/main/java/dev/vality/newway/dao/rate/impl/RateDaoImpl.java b/src/main/java/dev/vality/newway/dao/rate/impl/RateDaoImpl.java index 8fbf84c3..6fcd54e3 100644 --- a/src/main/java/dev/vality/newway/dao/rate/impl/RateDaoImpl.java +++ b/src/main/java/dev/vality/newway/dao/rate/impl/RateDaoImpl.java @@ -42,7 +42,7 @@ public Long save(Rate rate) throws DaoException { @Override public List getIds(String sourceId) throws DaoException { return this.getNamedParameterJdbcTemplate() - .queryForList("select id from nw.rate where source_id=:source_id and current", + .queryForList("select id from dw.rate where source_id=:source_id and current", new MapSqlParameterSource("source_id", sourceId), Long.class); } @@ -50,7 +50,7 @@ public List getIds(String sourceId) throws DaoException { public void updateNotCurrent(List ids) throws DaoException { if (ids != null && !ids.isEmpty()) { this.getNamedParameterJdbcTemplate() - .update("update nw.rate set current=false where id in (:ids)", + .update("update dw.rate set current=false where id in (:ids)", new MapSqlParameterSource("ids", ids)); } } diff --git a/src/main/java/dev/vality/newway/factory/cash/flow/CashFlowFactory.java b/src/main/java/dev/vality/newway/factory/cash/flow/CashFlowFactory.java new file mode 100644 index 00000000..44d8349c --- /dev/null +++ b/src/main/java/dev/vality/newway/factory/cash/flow/CashFlowFactory.java @@ -0,0 +1,69 @@ +package dev.vality.newway.factory.cash.flow; + +import dev.vality.damsel.domain.FinalCashFlowAccount; +import dev.vality.damsel.domain.FinalCashFlowPosting; +import dev.vality.geck.common.util.TypeUtil; +import dev.vality.newway.domain.enums.AdjustmentCashFlowType; +import dev.vality.newway.domain.enums.PaymentChangeType; +import dev.vality.newway.domain.tables.pojos.CashFlow; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.stream.Collectors; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class CashFlowFactory { + + public static List build(List cashFlowPostings, Long objId, + PaymentChangeType paymentChangeType) { + return build(cashFlowPostings, objId, paymentChangeType, null); + } + + public static List build(List cashFlowPostings, Long objId, + PaymentChangeType paymentChangeType, + AdjustmentCashFlowType adjustmentCashFlowType) { + return cashFlowPostings.stream().map(cf -> { + CashFlow pcf = new CashFlow(); + pcf.setObjId(objId); + pcf.setObjType(paymentChangeType); + pcf.setAdjFlowType(adjustmentCashFlowType); + pcf.setSourceAccountType(getCashFlowAccountType(cf.getSource())); + pcf.setSourceAccountTypeValue(getCashFlowAccountTypeValue(cf.getSource())); + pcf.setSourceAccountId(cf.getSource().getAccountId()); + pcf.setDestinationAccountType(getCashFlowAccountType(cf.getDestination())); + pcf.setDestinationAccountTypeValue(getCashFlowAccountTypeValue(cf.getDestination())); + pcf.setDestinationAccountId(cf.getDestination().getAccountId()); + pcf.setAmount(cf.getVolume().getAmount()); + pcf.setCurrencyCode(cf.getVolume().getCurrency().getSymbolicCode()); + pcf.setDetails(cf.getDetails()); + return pcf; + }).collect(Collectors.toList()); + } + + private static dev.vality.newway.domain.enums.CashFlowAccount getCashFlowAccountType(FinalCashFlowAccount cfa) { + dev.vality.newway.domain.enums.CashFlowAccount sourceAccountType = + TypeUtil.toEnumField(cfa.getAccountType().getSetField().getFieldName(), dev.vality.newway.domain.enums.CashFlowAccount.class); + if (sourceAccountType == null) { + throw new IllegalArgumentException("Illegal cash flow account type: " + cfa.getAccountType()); + } + return sourceAccountType; + } + + private static String getCashFlowAccountTypeValue(FinalCashFlowAccount cfa) { + if (cfa.getAccountType().isSetMerchant()) { + return cfa.getAccountType().getMerchant().name(); + } else if (cfa.getAccountType().isSetProvider()) { + return cfa.getAccountType().getProvider().name(); + } else if (cfa.getAccountType().isSetSystem()) { + return cfa.getAccountType().getSystem().name(); + } else if (cfa.getAccountType().isSetExternal()) { + return cfa.getAccountType().getExternal().name(); + } else if (cfa.getAccountType().isSetWallet()) { + return cfa.getAccountType().getWallet().name(); + } else { + throw new IllegalArgumentException("Illegal cash flow account type: " + cfa.getAccountType()); + } + } + +} diff --git a/src/main/java/dev/vality/newway/factory/cash/flow/CashFlowLinkFactory.java b/src/main/java/dev/vality/newway/factory/cash/flow/CashFlowLinkFactory.java new file mode 100644 index 00000000..00c727dd --- /dev/null +++ b/src/main/java/dev/vality/newway/factory/cash/flow/CashFlowLinkFactory.java @@ -0,0 +1,26 @@ +package dev.vality.newway.factory.cash.flow; + +import dev.vality.newway.domain.tables.pojos.CashFlowLink; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class CashFlowLinkFactory { + + public static CashFlowLink build(String paymentId, + String invoiceId, + LocalDateTime eventCreatedAt, + Integer changeId, + Long sequenceId) { + CashFlowLink link = new CashFlowLink(); + link.setPaymentId(paymentId); + link.setInvoiceId(invoiceId); + link.setEventCreatedAt(eventCreatedAt); + link.setChangeId(changeId); + link.setSequenceId(sequenceId); + return link; + } + +} diff --git a/src/main/java/dev/vality/newway/util/ContractorUtil.java b/src/main/java/dev/vality/newway/factory/contractor/ContractorFactory.java similarity index 91% rename from src/main/java/dev/vality/newway/util/ContractorUtil.java rename to src/main/java/dev/vality/newway/factory/contractor/ContractorFactory.java index 3ef2b4a8..2a31ff4b 100644 --- a/src/main/java/dev/vality/newway/util/ContractorUtil.java +++ b/src/main/java/dev/vality/newway/factory/contractor/ContractorFactory.java @@ -1,4 +1,4 @@ -package dev.vality.newway.util; +package dev.vality.newway.factory.contractor; import dev.vality.damsel.domain.InternationalLegalEntity; import dev.vality.damsel.domain.RussianLegalEntity; @@ -9,11 +9,15 @@ import dev.vality.newway.domain.enums.LegalEntity; import dev.vality.newway.domain.enums.PrivateEntity; import dev.vality.newway.domain.tables.pojos.Contractor; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; -public class ContractorUtil { - public static Contractor convertContractor(long sequenceId, String eventCreatedAt, String partyId, - dev.vality.damsel.domain.Contractor contractorSource, - String contractorId, Integer changeId, Integer claimEffectId) { +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ContractorFactory { + + public static Contractor build(long sequenceId, String eventCreatedAt, String partyId, + dev.vality.damsel.domain.Contractor contractorSource, + String contractorId, Integer changeId, Integer claimEffectId) { Contractor contractor = new Contractor(); contractor.setSequenceId((int) sequenceId); contractor.setChangeId(changeId); diff --git a/src/main/java/dev/vality/newway/factory/invoice/payment/InvoicePaymentEventIdHolderFactory.java b/src/main/java/dev/vality/newway/factory/invoice/payment/InvoicePaymentEventIdHolderFactory.java new file mode 100644 index 00000000..a52e990e --- /dev/null +++ b/src/main/java/dev/vality/newway/factory/invoice/payment/InvoicePaymentEventIdHolderFactory.java @@ -0,0 +1,20 @@ +package dev.vality.newway.factory.invoice.payment; + +import dev.vality.newway.domain.tables.pojos.CashFlowLink; +import dev.vality.newway.model.InvoicePaymentEventIdHolder; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class InvoicePaymentEventIdHolderFactory { + + public static InvoicePaymentEventIdHolder build(CashFlowLink link) { + return new InvoicePaymentEventIdHolder( + link.getInvoiceId(), + link.getPaymentId(), + link.getSequenceId(), + link.getChangeId() + ); + } + +} diff --git a/src/main/java/dev/vality/newway/factory/invoice/payment/PaymentFeeFactory.java b/src/main/java/dev/vality/newway/factory/invoice/payment/PaymentFeeFactory.java new file mode 100644 index 00000000..b55d8fb0 --- /dev/null +++ b/src/main/java/dev/vality/newway/factory/invoice/payment/PaymentFeeFactory.java @@ -0,0 +1,37 @@ +package dev.vality.newway.factory.invoice.payment; + +import dev.vality.damsel.domain.FinalCashFlowPosting; +import dev.vality.newway.domain.tables.pojos.PaymentFee; +import dev.vality.newway.model.CashFlowType; +import dev.vality.newway.util.CashFlowUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class PaymentFeeFactory { + + public static PaymentFee build(List finalCashFlow, + String invoiceId, + String paymentId, + LocalDateTime eventCreatedAt, + Integer changeId, + Long sequenceId) { + PaymentFee paymentFee = new PaymentFee(); + paymentFee.setEventCreatedAt(eventCreatedAt); + paymentFee.setInvoiceId(invoiceId); + paymentFee.setPaymentId(paymentId); + Map parsedCashFlow = CashFlowUtil.parseCashFlow(finalCashFlow); + paymentFee.setFee(parsedCashFlow.getOrDefault(CashFlowType.FEE, 0L)); + paymentFee.setProviderFee(parsedCashFlow.getOrDefault(CashFlowType.PROVIDER_FEE, 0L)); + paymentFee.setExternalFee(parsedCashFlow.getOrDefault(CashFlowType.EXTERNAL_FEE, 0L)); + paymentFee.setGuaranteeDeposit(parsedCashFlow.getOrDefault(CashFlowType.GUARANTEE_DEPOSIT, 0L)); + paymentFee.setSequenceId(sequenceId); + paymentFee.setChangeId(changeId); + return paymentFee; + } + +} diff --git a/src/main/java/dev/vality/newway/factory/invoice/payment/PaymentRouteFactory.java b/src/main/java/dev/vality/newway/factory/invoice/payment/PaymentRouteFactory.java new file mode 100644 index 00000000..b583e47d --- /dev/null +++ b/src/main/java/dev/vality/newway/factory/invoice/payment/PaymentRouteFactory.java @@ -0,0 +1,29 @@ +package dev.vality.newway.factory.invoice.payment; + +import dev.vality.newway.domain.tables.pojos.PaymentRoute; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class PaymentRouteFactory { + + public static PaymentRoute build(dev.vality.damsel.domain.PaymentRoute paymentRouteSource, + String invoiceId, + String paymentId, + LocalDateTime eventCreatedAt, + Integer changeId, + Long sequenceId) { + PaymentRoute paymentRoute = new PaymentRoute(); + paymentRoute.setEventCreatedAt(eventCreatedAt); + paymentRoute.setInvoiceId(invoiceId); + paymentRoute.setPaymentId(paymentId); + paymentRoute.setRouteProviderId(paymentRouteSource.getProvider().getId()); + paymentRoute.setRouteTerminalId(paymentRouteSource.getTerminal().getId()); + paymentRoute.setSequenceId(sequenceId); + paymentRoute.setChangeId(changeId); + return paymentRoute; + } + +} diff --git a/src/main/java/dev/vality/newway/factory/invoice/payment/PaymentStatusInfoFactory.java b/src/main/java/dev/vality/newway/factory/invoice/payment/PaymentStatusInfoFactory.java new file mode 100644 index 00000000..f6895af1 --- /dev/null +++ b/src/main/java/dev/vality/newway/factory/invoice/payment/PaymentStatusInfoFactory.java @@ -0,0 +1,45 @@ +package dev.vality.newway.factory.invoice.payment; + +import dev.vality.damsel.domain.InvoicePaymentCaptured; +import dev.vality.damsel.domain.InvoicePaymentStatus; +import dev.vality.geck.common.util.TBaseUtil; +import dev.vality.newway.domain.enums.PaymentStatus; +import dev.vality.newway.domain.tables.pojos.PaymentStatusInfo; +import dev.vality.newway.util.JsonUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class PaymentStatusInfoFactory { + + public static PaymentStatusInfo build(InvoicePaymentStatus status, + String invoiceId, + String paymentId, + LocalDateTime eventCreatedAt, + Integer changeId, + Long sequenceId) { + PaymentStatusInfo statusInfo = new PaymentStatusInfo(); + statusInfo.setInvoiceId(invoiceId); + statusInfo.setPaymentId(paymentId); + statusInfo.setStatus(TBaseUtil.unionFieldToEnum(status, PaymentStatus.class)); + statusInfo.setEventCreatedAt(eventCreatedAt); + if (status.isSetCancelled()) { + statusInfo.setReason(status.getCancelled().getReason()); + } else if (status.isSetFailed()) { + statusInfo.setReason(JsonUtil.thriftBaseToJsonString(status.getFailed())); + } else if (status.isSetCaptured()) { + InvoicePaymentCaptured invoicePaymentCaptured = status.getCaptured(); + statusInfo.setReason(invoicePaymentCaptured.getReason()); + if (invoicePaymentCaptured.isSetCost()) { + statusInfo.setAmount(invoicePaymentCaptured.getCost().getAmount()); + statusInfo.setCurrencyCode(invoicePaymentCaptured.getCost().getCurrency().getSymbolicCode()); + } + } + statusInfo.setChangeId(changeId); + statusInfo.setSequenceId(sequenceId); + return statusInfo; + } + +} diff --git a/src/main/java/dev/vality/newway/factory/AdjustmentMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/AdjustmentMachineEventCopyFactoryImpl.java similarity index 95% rename from src/main/java/dev/vality/newway/factory/AdjustmentMachineEventCopyFactoryImpl.java rename to src/main/java/dev/vality/newway/factory/machine/event/AdjustmentMachineEventCopyFactoryImpl.java index 4a4b2289..dc975c9d 100644 --- a/src/main/java/dev/vality/newway/factory/AdjustmentMachineEventCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/AdjustmentMachineEventCopyFactoryImpl.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/factory/ChallengeMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/ChallengeMachineEventCopyFactoryImpl.java similarity index 96% rename from src/main/java/dev/vality/newway/factory/ChallengeMachineEventCopyFactoryImpl.java rename to src/main/java/dev/vality/newway/factory/machine/event/ChallengeMachineEventCopyFactoryImpl.java index 39557103..69ef6036 100644 --- a/src/main/java/dev/vality/newway/factory/ChallengeMachineEventCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/ChallengeMachineEventCopyFactoryImpl.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/factory/ChargebackMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/ChargebackMachineEventCopyFactoryImpl.java similarity index 95% rename from src/main/java/dev/vality/newway/factory/ChargebackMachineEventCopyFactoryImpl.java rename to src/main/java/dev/vality/newway/factory/machine/event/ChargebackMachineEventCopyFactoryImpl.java index a2c2d793..b7d8ebbc 100644 --- a/src/main/java/dev/vality/newway/factory/ChargebackMachineEventCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/ChargebackMachineEventCopyFactoryImpl.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/factory/DepositAjustmentMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/DepositAjustmentMachineEventCopyFactoryImpl.java similarity index 96% rename from src/main/java/dev/vality/newway/factory/DepositAjustmentMachineEventCopyFactoryImpl.java rename to src/main/java/dev/vality/newway/factory/machine/event/DepositAjustmentMachineEventCopyFactoryImpl.java index 60fcc30e..cdc37411 100644 --- a/src/main/java/dev/vality/newway/factory/DepositAjustmentMachineEventCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/DepositAjustmentMachineEventCopyFactoryImpl.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/factory/DepositMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/DepositMachineEventCopyFactoryImpl.java similarity index 96% rename from src/main/java/dev/vality/newway/factory/DepositMachineEventCopyFactoryImpl.java rename to src/main/java/dev/vality/newway/factory/machine/event/DepositMachineEventCopyFactoryImpl.java index b38a5c35..7b833030 100644 --- a/src/main/java/dev/vality/newway/factory/DepositMachineEventCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/DepositMachineEventCopyFactoryImpl.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/factory/DepositRevertMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/DepositRevertMachineEventCopyFactoryImpl.java similarity index 96% rename from src/main/java/dev/vality/newway/factory/DepositRevertMachineEventCopyFactoryImpl.java rename to src/main/java/dev/vality/newway/factory/machine/event/DepositRevertMachineEventCopyFactoryImpl.java index 736dd686..6ebb37ff 100644 --- a/src/main/java/dev/vality/newway/factory/DepositRevertMachineEventCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/DepositRevertMachineEventCopyFactoryImpl.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/factory/DestinationMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/DestinationMachineEventCopyFactoryImpl.java similarity index 96% rename from src/main/java/dev/vality/newway/factory/DestinationMachineEventCopyFactoryImpl.java rename to src/main/java/dev/vality/newway/factory/machine/event/DestinationMachineEventCopyFactoryImpl.java index 30648faf..7b9a692e 100644 --- a/src/main/java/dev/vality/newway/factory/DestinationMachineEventCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/DestinationMachineEventCopyFactoryImpl.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/factory/IdentityMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/IdentityMachineEventCopyFactoryImpl.java similarity index 95% rename from src/main/java/dev/vality/newway/factory/IdentityMachineEventCopyFactoryImpl.java rename to src/main/java/dev/vality/newway/factory/machine/event/IdentityMachineEventCopyFactoryImpl.java index 71d5238b..a8fd1244 100644 --- a/src/main/java/dev/vality/newway/factory/IdentityMachineEventCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/IdentityMachineEventCopyFactoryImpl.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/factory/MachineEventCopyFactory.java b/src/main/java/dev/vality/newway/factory/machine/event/MachineEventCopyFactory.java similarity index 84% rename from src/main/java/dev/vality/newway/factory/MachineEventCopyFactory.java rename to src/main/java/dev/vality/newway/factory/machine/event/MachineEventCopyFactory.java index e8f363f2..7736997a 100644 --- a/src/main/java/dev/vality/newway/factory/MachineEventCopyFactory.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/MachineEventCopyFactory.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/factory/PartyMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/PartyMachineEventCopyFactoryImpl.java similarity index 95% rename from src/main/java/dev/vality/newway/factory/PartyMachineEventCopyFactoryImpl.java rename to src/main/java/dev/vality/newway/factory/machine/event/PartyMachineEventCopyFactoryImpl.java index 128b6059..bcbb8df2 100644 --- a/src/main/java/dev/vality/newway/factory/PartyMachineEventCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/PartyMachineEventCopyFactoryImpl.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/factory/RecurrentPaymentToolCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/RecurrentPaymentToolCopyFactoryImpl.java similarity index 96% rename from src/main/java/dev/vality/newway/factory/RecurrentPaymentToolCopyFactoryImpl.java rename to src/main/java/dev/vality/newway/factory/machine/event/RecurrentPaymentToolCopyFactoryImpl.java index f1b1c457..a5dd50e3 100644 --- a/src/main/java/dev/vality/newway/factory/RecurrentPaymentToolCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/RecurrentPaymentToolCopyFactoryImpl.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/factory/RefundMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/RefundMachineEventCopyFactoryImpl.java similarity index 95% rename from src/main/java/dev/vality/newway/factory/RefundMachineEventCopyFactoryImpl.java rename to src/main/java/dev/vality/newway/factory/machine/event/RefundMachineEventCopyFactoryImpl.java index 787cb4c6..5a63295e 100644 --- a/src/main/java/dev/vality/newway/factory/RefundMachineEventCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/RefundMachineEventCopyFactoryImpl.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/factory/SourceMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/SourceMachineEventCopyFactoryImpl.java similarity index 95% rename from src/main/java/dev/vality/newway/factory/SourceMachineEventCopyFactoryImpl.java rename to src/main/java/dev/vality/newway/factory/machine/event/SourceMachineEventCopyFactoryImpl.java index 93064b24..cc4292a1 100644 --- a/src/main/java/dev/vality/newway/factory/SourceMachineEventCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/SourceMachineEventCopyFactoryImpl.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/factory/WalletMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/WalletMachineEventCopyFactoryImpl.java similarity index 95% rename from src/main/java/dev/vality/newway/factory/WalletMachineEventCopyFactoryImpl.java rename to src/main/java/dev/vality/newway/factory/machine/event/WalletMachineEventCopyFactoryImpl.java index eedc085f..f40c2815 100644 --- a/src/main/java/dev/vality/newway/factory/WalletMachineEventCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/WalletMachineEventCopyFactoryImpl.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/factory/WithdrawalMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/WithdrawalMachineEventCopyFactoryImpl.java similarity index 96% rename from src/main/java/dev/vality/newway/factory/WithdrawalMachineEventCopyFactoryImpl.java rename to src/main/java/dev/vality/newway/factory/machine/event/WithdrawalMachineEventCopyFactoryImpl.java index 3be91301..aded5c65 100644 --- a/src/main/java/dev/vality/newway/factory/WithdrawalMachineEventCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/WithdrawalMachineEventCopyFactoryImpl.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/factory/WithdrawalSessionMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/WithdrawalSessionMachineEventCopyFactoryImpl.java similarity index 96% rename from src/main/java/dev/vality/newway/factory/WithdrawalSessionMachineEventCopyFactoryImpl.java rename to src/main/java/dev/vality/newway/factory/machine/event/WithdrawalSessionMachineEventCopyFactoryImpl.java index e678e388..67b357e5 100644 --- a/src/main/java/dev/vality/newway/factory/WithdrawalSessionMachineEventCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/WithdrawalSessionMachineEventCopyFactoryImpl.java @@ -1,4 +1,4 @@ -package dev.vality.newway.factory; +package dev.vality.newway.factory.machine.event; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/LocalStorage.java b/src/main/java/dev/vality/newway/handler/event/stock/LocalStorage.java deleted file mode 100644 index b4f9e0c8..00000000 --- a/src/main/java/dev/vality/newway/handler/event/stock/LocalStorage.java +++ /dev/null @@ -1,18 +0,0 @@ -package dev.vality.newway.handler.event.stock; - -import dev.vality.newway.model.InvoicingKey; - -import java.util.HashMap; -import java.util.Map; - -public class LocalStorage { - private Map map = new HashMap<>(); - - public Object get(InvoicingKey key) { - return map.get(key); - } - - public void put(InvoicingKey key, Object object) { - map.put(key, object); - } -} diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositCreatedHandler.java index ffe5dba7..17859f2e 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositCreatedHandler.java @@ -11,7 +11,7 @@ import dev.vality.newway.dao.deposit.iface.DepositDao; import dev.vality.newway.domain.enums.DepositStatus; import dev.vality.newway.domain.tables.pojos.Deposit; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositStatusChangedHandler.java index 03599b4d..25d96719 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositStatusChangedHandler.java @@ -15,7 +15,7 @@ import dev.vality.newway.domain.enums.FistfulCashFlowChangeType; import dev.vality.newway.domain.tables.pojos.Deposit; import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositTransferCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositTransferCreatedHandler.java index 4d619863..b7262101 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositTransferCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositTransferCreatedHandler.java @@ -14,7 +14,7 @@ import dev.vality.newway.domain.enums.FistfulCashFlowChangeType; import dev.vality.newway.domain.tables.pojos.Deposit; import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.util.FistfulCashFlowUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositTransferStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositTransferStatusChangedHandler.java index 1891ab64..efef023a 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositTransferStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/DepositTransferStatusChangedHandler.java @@ -15,7 +15,7 @@ import dev.vality.newway.domain.enums.FistfulCashFlowChangeType; import dev.vality.newway.domain.tables.pojos.Deposit; import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentCreatedHandler.java index 13b22be3..34affdae 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentCreatedHandler.java @@ -16,7 +16,7 @@ import dev.vality.newway.domain.enums.DepositStatus; import dev.vality.newway.domain.tables.pojos.Deposit; import dev.vality.newway.domain.tables.pojos.DepositAdjustment; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.deposit.DepositHandler; import dev.vality.newway.util.FistfulCashFlowUtil; import lombok.Getter; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentStatusChangedHandler.java index ff0a19b9..e809a768 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentStatusChangedHandler.java @@ -15,7 +15,7 @@ import dev.vality.newway.domain.enums.FistfulCashFlowChangeType; import dev.vality.newway.domain.tables.pojos.DepositAdjustment; import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.deposit.DepositHandler; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentTransferCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentTransferCreatedHandler.java index 11d068cc..8f3162cd 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentTransferCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentTransferCreatedHandler.java @@ -14,7 +14,7 @@ import dev.vality.newway.domain.enums.FistfulCashFlowChangeType; import dev.vality.newway.domain.tables.pojos.DepositAdjustment; import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.deposit.DepositHandler; import dev.vality.newway.util.FistfulCashFlowUtil; import lombok.Getter; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentTransferStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentTransferStatusChangedHandler.java index 4f92df29..e5bc7482 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentTransferStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/adjustment/DepositAdjustmentTransferStatusChangedHandler.java @@ -15,7 +15,7 @@ import dev.vality.newway.domain.enums.FistfulCashFlowChangeType; import dev.vality.newway.domain.tables.pojos.DepositAdjustment; import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.deposit.DepositHandler; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertCreatedHandler.java index 2b1e7824..9f8e240a 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertCreatedHandler.java @@ -12,7 +12,7 @@ import dev.vality.newway.dao.deposit.revert.iface.DepositRevertDao; import dev.vality.newway.domain.enums.DepositRevertStatus; import dev.vality.newway.domain.tables.pojos.DepositRevert; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.deposit.DepositHandler; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertStatusChangedHandler.java index 9b989d01..230ed616 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertStatusChangedHandler.java @@ -15,7 +15,7 @@ import dev.vality.newway.domain.enums.FistfulCashFlowChangeType; import dev.vality.newway.domain.tables.pojos.DepositRevert; import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.deposit.DepositHandler; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertTransferCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertTransferCreatedHandler.java index 70c450f8..76ee2aac 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertTransferCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertTransferCreatedHandler.java @@ -14,7 +14,7 @@ import dev.vality.newway.domain.enums.FistfulCashFlowChangeType; import dev.vality.newway.domain.tables.pojos.DepositRevert; import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.deposit.DepositHandler; import dev.vality.newway.util.FistfulCashFlowUtil; import lombok.Getter; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertTransferStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertTransferStatusChangedHandler.java index d884eceb..8fe800e6 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertTransferStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/deposit/revert/DepositRevertTransferStatusChangedHandler.java @@ -15,7 +15,7 @@ import dev.vality.newway.domain.enums.FistfulCashFlowChangeType; import dev.vality.newway.domain.tables.pojos.DepositRevert; import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.deposit.DepositHandler; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationAccountCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationAccountCreatedHandler.java index d12bd261..28179c5e 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationAccountCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationAccountCreatedHandler.java @@ -12,7 +12,7 @@ import dev.vality.newway.dao.identity.iface.IdentityDao; import dev.vality.newway.domain.tables.pojos.Destination; import dev.vality.newway.domain.tables.pojos.Identity; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedHandler.java index afcdccb3..dc08b2a4 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedHandler.java @@ -15,7 +15,7 @@ import dev.vality.newway.domain.enums.DestinationResourceType; import dev.vality.newway.domain.enums.DestinationStatus; import dev.vality.newway.domain.tables.pojos.Destination; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.util.JsonUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationStatusChangedHandler.java index 0321e836..da4b4485 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationStatusChangedHandler.java @@ -12,7 +12,7 @@ import dev.vality.newway.dao.destination.iface.DestinationDao; import dev.vality.newway.domain.enums.DestinationStatus; import dev.vality.newway.domain.tables.pojos.Destination; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityChallengeCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityChallengeCreatedHandler.java index 88009d4e..8cf7d576 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityChallengeCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityChallengeCreatedHandler.java @@ -12,7 +12,7 @@ import dev.vality.newway.dao.identity.iface.ChallengeDao; import dev.vality.newway.domain.enums.ChallengeStatus; import dev.vality.newway.domain.tables.pojos.Challenge; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.util.JsonUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityChallengeStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityChallengeStatusChangedHandler.java index ed833f87..836665c3 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityChallengeStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityChallengeStatusChangedHandler.java @@ -10,7 +10,7 @@ import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.dao.identity.iface.ChallengeDao; import dev.vality.newway.domain.tables.pojos.Challenge; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityCreatedHandler.java index 27e441e3..3002942c 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityCreatedHandler.java @@ -10,7 +10,7 @@ import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.dao.identity.iface.IdentityDao; import dev.vality.newway.domain.tables.pojos.Identity; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.util.JsonUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityEffectiveChallengeChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityEffectiveChallengeChangedHandler.java index 258e9cba..ab93ed77 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityEffectiveChallengeChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityEffectiveChallengeChangedHandler.java @@ -9,7 +9,7 @@ import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.dao.identity.iface.IdentityDao; import dev.vality.newway.domain.tables.pojos.Identity; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityLevelChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityLevelChangedHandler.java index 42122851..add147a7 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityLevelChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/identity/IdentityLevelChangedHandler.java @@ -9,7 +9,7 @@ import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.dao.identity.iface.IdentityDao; import dev.vality.newway.domain.tables.pojos.Identity; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/adjustment/InvoicePaymentAdjustmentCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/adjustment/InvoicePaymentAdjustmentCreatedHandler.java index a54d35a8..3e8e3942 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/adjustment/InvoicePaymentAdjustmentCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/adjustment/InvoicePaymentAdjustmentCreatedHandler.java @@ -22,10 +22,10 @@ import dev.vality.newway.domain.tables.pojos.Adjustment; import dev.vality.newway.domain.tables.pojos.CashFlow; import dev.vality.newway.domain.tables.pojos.Payment; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.cash.flow.CashFlowFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.invoicing.InvoicingHandler; import dev.vality.newway.util.AdjustmentUtils; -import dev.vality.newway.util.CashFlowUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -111,13 +111,13 @@ public void handle(InvoiceChange invoiceChange, MachineEvent event, Integer chan adjustmentDao.save(adjustment).ifPresentOrElse( id -> { - List newCashFlowList = CashFlowUtil.convertCashFlows( + List newCashFlowList = CashFlowFactory.build( invoicePaymentAdjustment.getNewCashFlow(), id, PaymentChangeType.adjustment, AdjustmentCashFlowType.new_cash_flow); cashFlowDao.save(newCashFlowList); - List oldCashFlowList = CashFlowUtil.convertCashFlows( + List oldCashFlowList = CashFlowFactory.build( invoicePaymentAdjustment.getOldCashFlowInverse(), id, PaymentChangeType.adjustment, diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/adjustment/InvoicePaymentAdjustmentStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/adjustment/InvoicePaymentAdjustmentStatusChangedHandler.java index ded4b1b5..5b549bdc 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/adjustment/InvoicePaymentAdjustmentStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/adjustment/InvoicePaymentAdjustmentStatusChangedHandler.java @@ -17,7 +17,7 @@ import dev.vality.newway.domain.enums.AdjustmentStatus; import dev.vality.newway.domain.tables.pojos.Adjustment; import dev.vality.newway.domain.tables.pojos.CashFlow; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.invoicing.InvoicingHandler; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackBodyChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackBodyChangedHandler.java index 620a18a4..c4610673 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackBodyChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackBodyChangedHandler.java @@ -12,7 +12,7 @@ import dev.vality.newway.dao.invoicing.iface.ChargebackDao; import dev.vality.newway.domain.enums.PaymentChangeType; import dev.vality.newway.domain.tables.pojos.Chargeback; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.invoicing.InvoicingHandler; import dev.vality.newway.service.CashFlowService; import lombok.Getter; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackCashFlowChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackCashFlowChangedHandler.java index 499e3a84..c70cf221 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackCashFlowChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackCashFlowChangedHandler.java @@ -14,10 +14,10 @@ import dev.vality.newway.domain.enums.PaymentChangeType; import dev.vality.newway.domain.tables.pojos.CashFlow; import dev.vality.newway.domain.tables.pojos.Chargeback; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.cash.flow.CashFlowFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.invoicing.InvoicingHandler; import dev.vality.newway.service.CashFlowService; -import dev.vality.newway.util.CashFlowUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -65,7 +65,7 @@ public void handle(InvoiceChange change, MachineEvent event, Integer changeId) { Long oldId = chargebackOld.getId(); chargebackDao.updateNotCurrent(oldId); cashFlowService.save(oldId, id, PaymentChangeType.chargeback); - List cashFlows = CashFlowUtil.convertCashFlows( + List cashFlows = CashFlowFactory.build( invoicePaymentChargebackCashFlowChanged.getCashFlow(), id, PaymentChangeType.chargeback); diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackCreatedHandler.java index 33fb4568..297079a4 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackCreatedHandler.java @@ -19,7 +19,7 @@ import dev.vality.newway.domain.enums.ChargebackStatus; import dev.vality.newway.domain.tables.pojos.Chargeback; import dev.vality.newway.domain.tables.pojos.Payment; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.invoicing.InvoicingHandler; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackLevyChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackLevyChangedHandler.java index 86294133..ba5192fb 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackLevyChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackLevyChangedHandler.java @@ -12,7 +12,7 @@ import dev.vality.newway.dao.invoicing.iface.ChargebackDao; import dev.vality.newway.domain.enums.PaymentChangeType; import dev.vality.newway.domain.tables.pojos.Chargeback; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.invoicing.InvoicingHandler; import dev.vality.newway.service.CashFlowService; import lombok.Getter; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackStageChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackStageChangedHandler.java index 3d8b83ad..4e93fee4 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackStageChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackStageChangedHandler.java @@ -14,7 +14,7 @@ import dev.vality.newway.domain.enums.ChargebackStage; import dev.vality.newway.domain.enums.PaymentChangeType; import dev.vality.newway.domain.tables.pojos.Chargeback; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.invoicing.InvoicingHandler; import dev.vality.newway.service.CashFlowService; import lombok.Getter; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackStatusChangedHandler.java index ebe1f23e..39f931e9 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/chargeback/InvoicePaymentChargebackStatusChangedHandler.java @@ -14,7 +14,7 @@ import dev.vality.newway.domain.enums.ChargebackStatus; import dev.vality.newway.domain.enums.PaymentChangeType; import dev.vality.newway.domain.tables.pojos.Chargeback; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.invoicing.InvoicingHandler; import dev.vality.newway.service.CashFlowService; import lombok.Getter; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/refund/InvoicePaymentRefundCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/refund/InvoicePaymentRefundCreatedHandler.java index 12caefa6..6ef785c1 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/refund/InvoicePaymentRefundCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/refund/InvoicePaymentRefundCreatedHandler.java @@ -20,9 +20,9 @@ import dev.vality.newway.domain.tables.pojos.CashFlow; import dev.vality.newway.domain.tables.pojos.Payment; import dev.vality.newway.domain.tables.pojos.Refund; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.cash.flow.CashFlowFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.invoicing.InvoicingHandler; -import dev.vality.newway.util.CashFlowUtil; import dev.vality.newway.util.JsonUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -99,8 +99,8 @@ public void handle(InvoiceChange invoiceChange, MachineEvent event, Integer chan refundDao.save(refund).ifPresentOrElse( id -> { - List cashFlowList = CashFlowUtil - .convertCashFlows(invoicePaymentRefundCreated.getCashFlow(), id, PaymentChangeType.refund); + List cashFlowList = CashFlowFactory.build( + invoicePaymentRefundCreated.getCashFlow(), id, PaymentChangeType.refund); cashFlowDao.save(cashFlowList); refundDao.updateCommissions(id); log.info("Refund has been saved, sequenceId={}, invoiceId={}, paymentId={}, refundId={}", diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/refund/InvoicePaymentRefundSessionChangeTransactionBoundHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/refund/InvoicePaymentRefundSessionChangeTransactionBoundHandler.java index 20e98886..45e90295 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/refund/InvoicePaymentRefundSessionChangeTransactionBoundHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/refund/InvoicePaymentRefundSessionChangeTransactionBoundHandler.java @@ -13,7 +13,7 @@ import dev.vality.newway.dao.invoicing.iface.RefundDao; import dev.vality.newway.domain.enums.PaymentChangeType; import dev.vality.newway.domain.tables.pojos.Refund; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.invoicing.InvoicingHandler; import dev.vality.newway.service.CashFlowService; import dev.vality.newway.util.JsonUtil; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/refund/InvoicePaymentRefundStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/refund/InvoicePaymentRefundStatusChangedHandler.java index 7acea134..c2f22d59 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/refund/InvoicePaymentRefundStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/invoicing/refund/InvoicePaymentRefundStatusChangedHandler.java @@ -14,7 +14,7 @@ import dev.vality.newway.domain.enums.PaymentChangeType; import dev.vality.newway.domain.enums.RefundStatus; import dev.vality.newway.domain.tables.pojos.Refund; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.invoicing.InvoicingHandler; import dev.vality.newway.service.CashFlowService; import dev.vality.newway.util.JsonUtil; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/contract/ContractCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/contract/ContractCreatedHandler.java index 2515e08b..fe1d8ebd 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/contract/ContractCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/contract/ContractCreatedHandler.java @@ -15,7 +15,7 @@ import dev.vality.newway.factory.claim.effect.ClaimEffectCopyFactory; import dev.vality.newway.handler.event.stock.impl.partymngmnt.AbstractClaimChangedHandler; import dev.vality.newway.util.ContractUtil; -import dev.vality.newway.util.ContractorUtil; +import dev.vality.newway.factory.contractor.ContractorFactory; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.core.annotation.Order; @@ -118,7 +118,7 @@ private void updateContractReference(MachineEvent event, Integer changeId, long String contractId, String partyId, String contractorId, Long cntrctId, Integer claimEffectId) { if (contractCreated.isSetContractor()) { - Contractor contractor = ContractorUtil.convertContractor(sequenceId, event.getCreatedAt(), + Contractor contractor = ContractorFactory.build(sequenceId, event.getCreatedAt(), partyId, contractCreated.getContractor(), contractorId, changeId, claimEffectId); contractorDao.save(contractor); } diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/contractor/ContractorCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/contractor/ContractorCreatedHandler.java index c00a796b..0b13a8b6 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/contractor/ContractorCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/contractor/ContractorCreatedHandler.java @@ -9,7 +9,7 @@ import dev.vality.newway.dao.party.iface.PartyDao; import dev.vality.newway.domain.tables.pojos.Contractor; import dev.vality.newway.handler.event.stock.impl.partymngmnt.AbstractClaimChangedHandler; -import dev.vality.newway.util.ContractorUtil; +import dev.vality.newway.factory.contractor.ContractorFactory; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.core.annotation.Order; @@ -55,7 +55,7 @@ private void handleEvent(MachineEvent event, Integer changeId, long eventId, lon eventId, partyId, contractorId); partyDao.get(partyId); //check party is exist - Contractor contractor = ContractorUtil.convertContractor( + Contractor contractor = ContractorFactory.build( eventId, event.getCreatedAt(), partyId, contractorCreated, contractorId, changeId, claimEffectId); contractor.setIdentificationalLevel(partyContractor.getStatus().name()); contractorDao.save(contractor).ifPresentOrElse( diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyBlockingHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyBlockingHandler.java index 9faf6a82..5c53a4ed 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyBlockingHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyBlockingHandler.java @@ -11,7 +11,7 @@ import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.dao.party.iface.PartyDao; import dev.vality.newway.domain.tables.pojos.Party; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.partymngmnt.PartyManagementHandler; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyCreatedHandler.java index 1dabd73e..006ad437 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyCreatedHandler.java @@ -12,7 +12,7 @@ import dev.vality.newway.domain.enums.Blocking; import dev.vality.newway.domain.enums.Suspension; import dev.vality.newway.domain.tables.pojos.Party; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.partymngmnt.PartyManagementHandler; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyMetaSetHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyMetaSetHandler.java index ec1d8fbf..8f231185 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyMetaSetHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyMetaSetHandler.java @@ -9,7 +9,7 @@ import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.dao.party.iface.PartyDao; import dev.vality.newway.domain.tables.pojos.Party; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.partymngmnt.PartyManagementHandler; import dev.vality.newway.util.JsonUtil; import lombok.Getter; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyRevisionChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyRevisionChangedHandler.java index 32275f28..365f9e52 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyRevisionChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartyRevisionChangedHandler.java @@ -11,7 +11,7 @@ import dev.vality.newway.dao.party.iface.PartyDao; import dev.vality.newway.dao.party.iface.RevisionDao; import dev.vality.newway.domain.tables.pojos.Party; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.partymngmnt.PartyManagementHandler; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartySuspensionHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartySuspensionHandler.java index 57c9a936..99df479f 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartySuspensionHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/partymngmnt/party/PartySuspensionHandler.java @@ -11,7 +11,7 @@ import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.dao.party.iface.PartyDao; import dev.vality.newway.domain.tables.pojos.Party; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.partymngmnt.PartyManagementHandler; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasAbandonedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasAbandonedHandler.java index 87927c7f..33931f02 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasAbandonedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasAbandonedHandler.java @@ -9,7 +9,7 @@ import dev.vality.newway.dao.recurrent.payment.tool.iface.RecurrentPaymentToolDao; import dev.vality.newway.domain.enums.RecurrentPaymentToolStatus; import dev.vality.newway.domain.tables.pojos.RecurrentPaymentTool; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasAcquiredHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasAcquiredHandler.java index 79c53bec..951e3953 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasAcquiredHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasAcquiredHandler.java @@ -9,7 +9,7 @@ import dev.vality.newway.dao.recurrent.payment.tool.iface.RecurrentPaymentToolDao; import dev.vality.newway.domain.enums.RecurrentPaymentToolStatus; import dev.vality.newway.domain.tables.pojos.RecurrentPaymentTool; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasCreatedHandler.java index adcf593b..f67ce263 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasCreatedHandler.java @@ -14,7 +14,7 @@ import dev.vality.newway.domain.enums.PaymentToolType; import dev.vality.newway.domain.enums.RecurrentPaymentToolStatus; import dev.vality.newway.domain.tables.pojos.RecurrentPaymentTool; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.util.JsonUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasFailedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasFailedHandler.java index b78fcf0c..2ddde637 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasFailedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolHasFailedHandler.java @@ -9,7 +9,7 @@ import dev.vality.newway.dao.recurrent.payment.tool.iface.RecurrentPaymentToolDao; import dev.vality.newway.domain.enums.RecurrentPaymentToolStatus; import dev.vality.newway.domain.tables.pojos.RecurrentPaymentTool; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.util.JsonUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolRiskScoreChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolRiskScoreChangedHandler.java index d7915f37..c1afef15 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolRiskScoreChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolRiskScoreChangedHandler.java @@ -8,7 +8,7 @@ import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.dao.recurrent.payment.tool.iface.RecurrentPaymentToolDao; import dev.vality.newway.domain.tables.pojos.RecurrentPaymentTool; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolRouteChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolRouteChangedHandler.java index 98ecdb53..19c1ae30 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolRouteChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolRouteChangedHandler.java @@ -8,7 +8,7 @@ import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.dao.recurrent.payment.tool.iface.RecurrentPaymentToolDao; import dev.vality.newway.domain.tables.pojos.RecurrentPaymentTool; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolSessionChangedTransactionBoundHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolSessionChangedTransactionBoundHandler.java index fc7b58fd..37faf404 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolSessionChangedTransactionBoundHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/recurrent/payment/tool/RecurrentPaymentToolSessionChangedTransactionBoundHandler.java @@ -9,7 +9,7 @@ import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.dao.recurrent.payment.tool.iface.RecurrentPaymentToolDao; import dev.vality.newway.domain.tables.pojos.RecurrentPaymentTool; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.util.JsonUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/source/SourceAccountCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/source/SourceAccountCreatedHandler.java index be93dc27..9b1e930d 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/source/SourceAccountCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/source/SourceAccountCreatedHandler.java @@ -12,7 +12,7 @@ import dev.vality.newway.dao.source.iface.SourceDao; import dev.vality.newway.domain.tables.pojos.Identity; import dev.vality.newway.domain.tables.pojos.Source; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/source/SourceCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/source/SourceCreatedHandler.java index 9fd4de57..5e9ce9e6 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/source/SourceCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/source/SourceCreatedHandler.java @@ -12,7 +12,7 @@ import dev.vality.newway.dao.source.iface.SourceDao; import dev.vality.newway.domain.enums.SourceStatus; import dev.vality.newway.domain.tables.pojos.Source; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/source/SourceStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/source/SourceStatusChangedHandler.java index 276ccfe6..5cfa662a 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/source/SourceStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/source/SourceStatusChangedHandler.java @@ -12,7 +12,7 @@ import dev.vality.newway.dao.source.iface.SourceDao; import dev.vality.newway.domain.enums.SourceStatus; import dev.vality.newway.domain.tables.pojos.Source; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/wallet/WalletAccountCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/wallet/WalletAccountCreatedHandler.java index 6ff4d804..2e2a900d 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/wallet/WalletAccountCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/wallet/WalletAccountCreatedHandler.java @@ -12,7 +12,7 @@ import dev.vality.newway.dao.wallet.iface.WalletDao; import dev.vality.newway.domain.tables.pojos.Identity; import dev.vality.newway.domain.tables.pojos.Wallet; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/wallet/WalletCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/wallet/WalletCreatedHandler.java index 6c7adc87..5094f0df 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/wallet/WalletCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/wallet/WalletCreatedHandler.java @@ -9,7 +9,7 @@ import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.dao.wallet.iface.WalletDao; import dev.vality.newway.domain.tables.pojos.Wallet; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalCreatedHandler.java index 71e8561e..10f4faaa 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalCreatedHandler.java @@ -11,7 +11,7 @@ import dev.vality.newway.dao.withdrawal.iface.WithdrawalDao; import dev.vality.newway.domain.enums.WithdrawalStatus; import dev.vality.newway.domain.tables.pojos.Withdrawal; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalRouteChangeHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalRouteChangeHandler.java index 06997275..1516eeba 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalRouteChangeHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalRouteChangeHandler.java @@ -13,7 +13,7 @@ import dev.vality.newway.domain.enums.FistfulCashFlowChangeType; import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; import dev.vality.newway.domain.tables.pojos.Withdrawal; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalStatusChangedHandler.java index 2043215f..bb8c97b5 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalStatusChangedHandler.java @@ -15,7 +15,7 @@ import dev.vality.newway.domain.enums.WithdrawalStatus; import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; import dev.vality.newway.domain.tables.pojos.Withdrawal; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.util.JsonUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalTransferCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalTransferCreatedHandler.java index b712bc2e..1beb5b0b 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalTransferCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalTransferCreatedHandler.java @@ -14,7 +14,7 @@ import dev.vality.newway.domain.enums.WithdrawalTransferStatus; import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; import dev.vality.newway.domain.tables.pojos.Withdrawal; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.util.FistfulCashFlowUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalTransferStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalTransferStatusChangedHandler.java index c0915894..4cd1518d 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalTransferStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalTransferStatusChangedHandler.java @@ -15,7 +15,7 @@ import dev.vality.newway.domain.enums.WithdrawalTransferStatus; import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; import dev.vality.newway.domain.tables.pojos.Withdrawal; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedHandler.java index f04e1aad..b340a287 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedHandler.java @@ -16,7 +16,7 @@ import dev.vality.newway.domain.enums.DestinationResourceType; import dev.vality.newway.domain.enums.WithdrawalSessionStatus; import dev.vality.newway.domain.tables.pojos.WithdrawalSession; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionFinishedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionFinishedHandler.java index 93dd4007..898a8464 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionFinishedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionFinishedHandler.java @@ -11,7 +11,7 @@ import dev.vality.newway.dao.withdrawal.session.iface.WithdrawalSessionDao; import dev.vality.newway.domain.enums.WithdrawalSessionStatus; import dev.vality.newway.domain.tables.pojos.WithdrawalSession; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.util.JsonUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionNextStateHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionNextStateHandler.java index 26891fa1..f688f46f 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionNextStateHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionNextStateHandler.java @@ -9,7 +9,7 @@ import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.dao.withdrawal.session.iface.WithdrawalSessionDao; import dev.vality.newway.domain.tables.pojos.WithdrawalSession; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.util.JsonUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionTransactionBoundHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionTransactionBoundHandler.java index 8fd769c4..a26a80a2 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionTransactionBoundHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionTransactionBoundHandler.java @@ -12,7 +12,7 @@ import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.dao.withdrawal.session.iface.WithdrawalSessionDao; import dev.vality.newway.domain.tables.pojos.WithdrawalSession; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.util.JsonUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/dev/vality/newway/handler/wrapper/WrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/WrapperHandler.java new file mode 100644 index 00000000..0aca84eb --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/wrapper/WrapperHandler.java @@ -0,0 +1,11 @@ +package dev.vality.newway.handler.wrapper; + +import java.util.List; + +public interface WrapperHandler { + + boolean accept(List wrappers); + + void saveBatch(List wrappers); + +} diff --git a/src/main/java/dev/vality/newway/handler/wrapper/invoice/InvoiceCartWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/invoice/InvoiceCartWrapperHandler.java new file mode 100644 index 00000000..7c3d724a --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/wrapper/invoice/InvoiceCartWrapperHandler.java @@ -0,0 +1,47 @@ +package dev.vality.newway.handler.wrapper.invoice; + +import dev.vality.newway.dao.invoicing.iface.InvoiceCartDao; +import dev.vality.newway.domain.tables.pojos.InvoiceCart; +import dev.vality.newway.handler.wrapper.WrapperHandler; +import dev.vality.newway.model.InvoiceWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Component +public class InvoiceCartWrapperHandler implements WrapperHandler { + + private final InvoiceCartDao invoiceCartDao; + + @Override + public boolean accept(List wrappers) { + return wrappers.stream() + .map(InvoiceWrapper::getCarts) + .anyMatch(invoiceCarts -> !CollectionUtils.isEmpty(invoiceCarts)); + } + + @Override + public void saveBatch(List wrappers) { + List carts = wrappers.stream() + .map(InvoiceWrapper::getCarts) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + Set invoiceIds = carts.stream() + .map(InvoiceCart::getInvoiceId) + .collect(Collectors.toSet()); + Set existingInvoiceIds = invoiceCartDao.getExistingInvoiceIds(invoiceIds); + carts.removeIf(cart -> existingInvoiceIds.contains(cart.getInvoiceId())); + if (!carts.isEmpty()) { + invoiceCartDao.save(carts); + } + } + +} diff --git a/src/main/java/dev/vality/newway/handler/wrapper/invoice/InvoiceStatusInfoWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/invoice/InvoiceStatusInfoWrapperHandler.java new file mode 100644 index 00000000..d997a7fc --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/wrapper/invoice/InvoiceStatusInfoWrapperHandler.java @@ -0,0 +1,53 @@ +package dev.vality.newway.handler.wrapper.invoice; + +import dev.vality.newway.dao.invoicing.iface.InvoiceStatusInfoDao; +import dev.vality.newway.domain.tables.pojos.InvoiceStatusInfo; +import dev.vality.newway.handler.wrapper.WrapperHandler; +import dev.vality.newway.model.InvoiceWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +@RequiredArgsConstructor +@Component +public class InvoiceStatusInfoWrapperHandler implements WrapperHandler { + + private final InvoiceStatusInfoDao invoiceStatusInfoDao; + + @Override + public boolean accept(List wrappers) { + return wrappers.stream() + .map(InvoiceWrapper::getInvoiceStatusInfo) + .anyMatch(Objects::nonNull); + } + + @Override + public void saveBatch(List wrappers) { + saveInvoiceStatusInfos(wrappers); + switchCurrent(wrappers); + } + + private void saveInvoiceStatusInfos(List wrappers) { + List invoiceStatusInfos = wrappers.stream() + .map(InvoiceWrapper::getInvoiceStatusInfo) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + invoiceStatusInfoDao.saveBatch(invoiceStatusInfos); + } + + private void switchCurrent(List wrappers) { + Set invoiceStatusInfoChangedIds = wrappers.stream() + .map(InvoiceWrapper::getInvoiceStatusInfo) + .filter(Objects::nonNull) + .map(InvoiceStatusInfo::getInvoiceId) + .collect(Collectors.toSet()); + log.info("Switch to current ids - invoiceStatusInfo:{}", invoiceStatusInfoChangedIds); + invoiceStatusInfoDao.switchCurrent(invoiceStatusInfoChangedIds); + } +} diff --git a/src/main/java/dev/vality/newway/handler/wrapper/invoice/InvoiceWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/invoice/InvoiceWrapperHandler.java new file mode 100644 index 00000000..33bd512b --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/wrapper/invoice/InvoiceWrapperHandler.java @@ -0,0 +1,36 @@ +package dev.vality.newway.handler.wrapper.invoice; + +import dev.vality.newway.dao.invoicing.iface.InvoiceDao; +import dev.vality.newway.domain.tables.pojos.Invoice; +import dev.vality.newway.handler.wrapper.WrapperHandler; +import dev.vality.newway.model.InvoiceWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Component +public class InvoiceWrapperHandler implements WrapperHandler { + + private final InvoiceDao invoiceDao; + + @Override + public boolean accept(List wrappers) { + return wrappers.stream() + .map(InvoiceWrapper::getInvoice) + .anyMatch(Objects::nonNull); + } + + @Override + public void saveBatch(List wrappers) { + List invoices = wrappers.stream() + .map(InvoiceWrapper::getInvoice) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + invoiceDao.saveBatch(invoices); + } + +} diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/CashFlowWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/CashFlowWrapperHandler.java new file mode 100644 index 00000000..a42ef999 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/CashFlowWrapperHandler.java @@ -0,0 +1,96 @@ +package dev.vality.newway.handler.wrapper.payment; + +import dev.vality.newway.dao.invoicing.iface.CashFlowDao; +import dev.vality.newway.dao.invoicing.iface.CashFlowLinkDao; +import dev.vality.newway.dao.invoicing.impl.CashFlowLinkIdsGeneratorDaoImpl; +import dev.vality.newway.domain.tables.pojos.CashFlow; +import dev.vality.newway.domain.tables.pojos.CashFlowLink; +import dev.vality.newway.handler.wrapper.WrapperHandler; +import dev.vality.newway.model.CashFlowWrapper; +import dev.vality.newway.model.InvoicePaymentEventIdHolder; +import dev.vality.newway.model.InvoicingKey; +import dev.vality.newway.factory.invoice.payment.InvoicePaymentEventIdHolderFactory; +import dev.vality.newway.model.PaymentWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Component +public class CashFlowWrapperHandler implements WrapperHandler { + + private final CashFlowLinkDao cashFlowLinkDao; + + private final CashFlowDao cashFlowDao; + + private final CashFlowLinkIdsGeneratorDaoImpl idsGenerator; + + @Override + public boolean accept(List wrappers) { + return wrappers.stream() + .map(PaymentWrapper::getCashFlowWrapper) + .anyMatch(Objects::nonNull); + } + + @Override + public void saveBatch(List wrappers) { + List cashFlowWrappers = wrappers.stream() + .map(PaymentWrapper::getCashFlowWrapper) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + removeAlreadyProcessedWrappers(cashFlowWrappers); + if (CollectionUtils.isEmpty(wrappers)) { + return; + } + setLinkIds(cashFlowWrappers); + saveCashFlowLinks(cashFlowWrappers); + saveCashFlows(cashFlowWrappers); + } + + private void removeAlreadyProcessedWrappers(List cashFlowWrappers) { + Set existingEvents = cashFlowLinkDao.getExistingEvents( + cashFlowWrappers.stream() + .map(CashFlowWrapper::getCashFlowLink) + .filter(Objects::nonNull) + .collect(Collectors.toList()) + ); + cashFlowWrappers.removeIf(wrapper -> + existingEvents.contains(InvoicePaymentEventIdHolderFactory.build(wrapper.getCashFlowLink())) + ); + } + + private void setLinkIds(List cashFlowWrappers) { + Iterator linkIdIterator = idsGenerator.get(cashFlowWrappers.size()).iterator(); + for (CashFlowWrapper wrapper : cashFlowWrappers) { + Long linkId = linkIdIterator.next(); + wrapper.getCashFlowLink().setId(linkId); + for (CashFlow cashFlow : wrapper.getCashFlows()) { + cashFlow.setObjId(linkId); + } + } + } + + private void saveCashFlowLinks(List cashFlowWrappers) { + List links = cashFlowWrappers.stream() + .map(CashFlowWrapper::getCashFlowLink) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + cashFlowLinkDao.saveBatch(links); + Set invoicingKeys = links.stream() + .map(link -> InvoicingKey.buildKey(link.getInvoiceId(), link.getPaymentId())) + .collect(Collectors.toSet()); + cashFlowLinkDao.switchCurrent(invoicingKeys); + } + + private void saveCashFlows(List cashFlowWrappers) { + List cashFlows = cashFlowWrappers.stream() + .flatMap(wrapper -> wrapper.getCashFlows().stream()) + .collect(Collectors.toList()); + cashFlowDao.save(cashFlows); + } + +} diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentAdditionalInfoWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentAdditionalInfoWrapperHandler.java new file mode 100644 index 00000000..0f14e70d --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentAdditionalInfoWrapperHandler.java @@ -0,0 +1,37 @@ +package dev.vality.newway.handler.wrapper.payment; + +import dev.vality.newway.dao.invoicing.iface.PaymentAdditionalInfoDao; +import dev.vality.newway.domain.tables.pojos.PaymentAdditionalInfo; +import dev.vality.newway.handler.wrapper.WrapperHandler; +import dev.vality.newway.model.PaymentWrapper; +import dev.vality.newway.util.PaymentWrapperUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Component +public class PaymentAdditionalInfoWrapperHandler implements WrapperHandler { + + private final PaymentAdditionalInfoDao paymentAdditionalInfoDao; + + @Override + public boolean accept(List wrappers) { + return wrappers.stream() + .map(PaymentWrapper::getPaymentAdditionalInfo) + .anyMatch(Objects::nonNull); + } + + @Override + public void saveBatch(List wrappers) { + List paymentAdditionalInfos = wrappers.stream() + .map(PaymentWrapper::getPaymentAdditionalInfo) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + paymentAdditionalInfoDao.saveBatch(paymentAdditionalInfos); + paymentAdditionalInfoDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(wrappers)); + } +} diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentFeeWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentFeeWrapperHandler.java new file mode 100644 index 00000000..79b5fea8 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentFeeWrapperHandler.java @@ -0,0 +1,37 @@ +package dev.vality.newway.handler.wrapper.payment; + +import dev.vality.newway.dao.invoicing.iface.PaymentFeeDao; +import dev.vality.newway.domain.tables.pojos.PaymentFee; +import dev.vality.newway.handler.wrapper.WrapperHandler; +import dev.vality.newway.model.PaymentWrapper; +import dev.vality.newway.util.PaymentWrapperUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Component +public class PaymentFeeWrapperHandler implements WrapperHandler { + + private final PaymentFeeDao paymentFeeDao; + + @Override + public boolean accept(List wrappers) { + return wrappers.stream() + .map(PaymentWrapper::getPaymentFee) + .anyMatch(Objects::nonNull); + } + + @Override + public void saveBatch(List wrappers) { + List paymentFees = wrappers.stream() + .map(PaymentWrapper::getPaymentFee) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + paymentFeeDao.saveBatch(paymentFees); + paymentFeeDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(wrappers)); + } +} diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentPayerInfoWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentPayerInfoWrapperHandler.java new file mode 100644 index 00000000..046d2255 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentPayerInfoWrapperHandler.java @@ -0,0 +1,35 @@ +package dev.vality.newway.handler.wrapper.payment; + +import dev.vality.newway.dao.invoicing.iface.PaymentPayerInfoDao; +import dev.vality.newway.domain.tables.pojos.PaymentPayerInfo; +import dev.vality.newway.handler.wrapper.WrapperHandler; +import dev.vality.newway.model.PaymentWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Component +public class PaymentPayerInfoWrapperHandler implements WrapperHandler { + + private final PaymentPayerInfoDao paymentPayerInfoDao; + + @Override + public boolean accept(List wrappers) { + return wrappers.stream() + .map(PaymentWrapper::getPaymentPayerInfo) + .anyMatch(Objects::nonNull); + } + + @Override + public void saveBatch(List wrappers) { + List paymentPayerInfos = wrappers.stream() + .map(PaymentWrapper::getPaymentPayerInfo) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + paymentPayerInfoDao.saveBatch(paymentPayerInfos); + } +} diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRecurrentInfoWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRecurrentInfoWrapperHandler.java new file mode 100644 index 00000000..416cc4c9 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRecurrentInfoWrapperHandler.java @@ -0,0 +1,37 @@ +package dev.vality.newway.handler.wrapper.payment; + +import dev.vality.newway.dao.invoicing.iface.PaymentRecurrentInfoDao; +import dev.vality.newway.domain.tables.pojos.PaymentRecurrentInfo; +import dev.vality.newway.handler.wrapper.WrapperHandler; +import dev.vality.newway.model.PaymentWrapper; +import dev.vality.newway.util.PaymentWrapperUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Component +public class PaymentRecurrentInfoWrapperHandler implements WrapperHandler { + + private final PaymentRecurrentInfoDao paymentRecurrentInfoDao; + + @Override + public boolean accept(List wrappers) { + return wrappers.stream() + .map(PaymentWrapper::getPaymentRecurrentInfo) + .anyMatch(Objects::nonNull); + } + + @Override + public void saveBatch(List wrappers) { + List paymentRecurrentInfos = wrappers.stream() + .map(PaymentWrapper::getPaymentRecurrentInfo) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + paymentRecurrentInfoDao.saveBatch(paymentRecurrentInfos); + paymentRecurrentInfoDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(wrappers)); + } +} diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRiskDataWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRiskDataWrapperHandler.java new file mode 100644 index 00000000..02c79367 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRiskDataWrapperHandler.java @@ -0,0 +1,37 @@ +package dev.vality.newway.handler.wrapper.payment; + +import dev.vality.newway.dao.invoicing.iface.PaymentRiskDataDao; +import dev.vality.newway.domain.tables.pojos.PaymentRiskData; +import dev.vality.newway.handler.wrapper.WrapperHandler; +import dev.vality.newway.model.PaymentWrapper; +import dev.vality.newway.util.PaymentWrapperUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Component +public class PaymentRiskDataWrapperHandler implements WrapperHandler { + + private final PaymentRiskDataDao paymentRiskDataDao; + + @Override + public boolean accept(List wrappers) { + return wrappers.stream() + .map(PaymentWrapper::getPaymentRiskData) + .anyMatch(Objects::nonNull); + } + + @Override + public void saveBatch(List wrappers) { + List paymentRiskDataList = wrappers.stream() + .map(PaymentWrapper::getPaymentRiskData) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + paymentRiskDataDao.saveBatch(paymentRiskDataList); + paymentRiskDataDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(wrappers)); + } +} diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRouteWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRouteWrapperHandler.java new file mode 100644 index 00000000..873803f1 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRouteWrapperHandler.java @@ -0,0 +1,37 @@ +package dev.vality.newway.handler.wrapper.payment; + +import dev.vality.newway.dao.invoicing.iface.PaymentRouteDao; +import dev.vality.newway.domain.tables.pojos.PaymentRoute; +import dev.vality.newway.handler.wrapper.WrapperHandler; +import dev.vality.newway.model.PaymentWrapper; +import dev.vality.newway.util.PaymentWrapperUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Component +public class PaymentRouteWrapperHandler implements WrapperHandler { + + private final PaymentRouteDao paymentRouteDao; + + @Override + public boolean accept(List wrappers) { + return wrappers.stream() + .map(PaymentWrapper::getPaymentRoute) + .anyMatch(Objects::nonNull); + } + + @Override + public void saveBatch(List wrappers) { + List paymentRoutes = wrappers.stream() + .map(PaymentWrapper::getPaymentRoute) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + paymentRouteDao.saveBatch(paymentRoutes); + paymentRouteDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(wrappers)); + } +} diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentStatusInfoWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentStatusInfoWrapperHandler.java new file mode 100644 index 00000000..de891d95 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentStatusInfoWrapperHandler.java @@ -0,0 +1,37 @@ +package dev.vality.newway.handler.wrapper.payment; + +import dev.vality.newway.dao.invoicing.iface.PaymentStatusInfoDao; +import dev.vality.newway.domain.tables.pojos.PaymentStatusInfo; +import dev.vality.newway.handler.wrapper.WrapperHandler; +import dev.vality.newway.model.PaymentWrapper; +import dev.vality.newway.util.PaymentWrapperUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Component +public class PaymentStatusInfoWrapperHandler implements WrapperHandler { + + private final PaymentStatusInfoDao paymentStatusInfoDao; + + @Override + public boolean accept(List wrappers) { + return wrappers.stream() + .map(PaymentWrapper::getPaymentStatusInfo) + .anyMatch(Objects::nonNull); + } + + @Override + public void saveBatch(List wrappers) { + List paymentStatusInfos = wrappers.stream() + .map(PaymentWrapper::getPaymentStatusInfo) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + paymentStatusInfoDao.saveBatch(paymentStatusInfos); + paymentStatusInfoDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(wrappers)); + } +} diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentWrapperHandler.java new file mode 100644 index 00000000..c7cf2af0 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentWrapperHandler.java @@ -0,0 +1,35 @@ +package dev.vality.newway.handler.wrapper.payment; + +import dev.vality.newway.dao.invoicing.iface.PaymentDao; +import dev.vality.newway.domain.tables.pojos.Payment; +import dev.vality.newway.handler.wrapper.WrapperHandler; +import dev.vality.newway.model.PaymentWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Component +public class PaymentWrapperHandler implements WrapperHandler { + + private final PaymentDao paymentDao; + + @Override + public boolean accept(List wrappers) { + return wrappers.stream() + .map(PaymentWrapper::getPayment) + .anyMatch(Objects::nonNull); + } + + @Override + public void saveBatch(List wrappers) { + List payments = wrappers.stream() + .map(PaymentWrapper::getPayment) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + paymentDao.saveBatch(payments); + } +} diff --git a/src/main/java/dev/vality/newway/mapper/AbstractInvoicingMapper.java b/src/main/java/dev/vality/newway/mapper/AbstractInvoicingMapper.java deleted file mode 100644 index 66e90aab..00000000 --- a/src/main/java/dev/vality/newway/mapper/AbstractInvoicingMapper.java +++ /dev/null @@ -1,7 +0,0 @@ -package dev.vality.newway.mapper; - -import dev.vality.damsel.payment_processing.InvoiceChange; -import dev.vality.machinegun.eventsink.MachineEvent; - -public abstract class AbstractInvoicingMapper implements Mapper { -} diff --git a/src/main/java/dev/vality/newway/mapper/Mapper.java b/src/main/java/dev/vality/newway/mapper/Mapper.java index cce261bd..931830c6 100644 --- a/src/main/java/dev/vality/newway/mapper/Mapper.java +++ b/src/main/java/dev/vality/newway/mapper/Mapper.java @@ -1,14 +1,15 @@ package dev.vality.newway.mapper; +import dev.vality.damsel.payment_processing.InvoiceChange; import dev.vality.geck.filter.Filter; -import dev.vality.newway.handler.event.stock.LocalStorage; +import dev.vality.machinegun.eventsink.MachineEvent; -public interface Mapper { - default boolean accept(C change) { +public interface Mapper { + default boolean accept(InvoiceChange change) { return getFilter().match(change); } - M map(C change, E event, Integer changeId, LocalStorage storage); + M map(InvoiceChange change, MachineEvent event, Integer changeId); Filter getFilter(); } diff --git a/src/main/java/dev/vality/newway/mapper/invoice/AbstractInvoicingInvoiceMapper.java b/src/main/java/dev/vality/newway/mapper/invoice/AbstractInvoicingInvoiceMapper.java deleted file mode 100644 index bc9fd4f5..00000000 --- a/src/main/java/dev/vality/newway/mapper/invoice/AbstractInvoicingInvoiceMapper.java +++ /dev/null @@ -1,17 +0,0 @@ -package dev.vality.newway.mapper.invoice; - -import dev.vality.geck.common.util.TypeUtil; -import dev.vality.newway.domain.tables.pojos.Invoice; -import dev.vality.newway.mapper.AbstractInvoicingMapper; -import dev.vality.newway.model.InvoiceWrapper; - -public abstract class AbstractInvoicingInvoiceMapper extends AbstractInvoicingMapper { - protected void setDefaultProperties(Invoice invoice, Long sequenceId, Integer changeId, String eventCreatedAt) { - invoice.setId(null); - invoice.setWtime(null); - invoice.setCurrent(false); - invoice.setChangeId(changeId); - invoice.setSequenceId(sequenceId); - invoice.setEventCreatedAt(TypeUtil.stringToLocalDateTime(eventCreatedAt)); - } -} diff --git a/src/main/java/dev/vality/newway/mapper/invoice/InvoiceCreatedMapper.java b/src/main/java/dev/vality/newway/mapper/invoice/InvoiceCreatedMapper.java index 511cce96..f17a5687 100644 --- a/src/main/java/dev/vality/newway/mapper/invoice/InvoiceCreatedMapper.java +++ b/src/main/java/dev/vality/newway/mapper/invoice/InvoiceCreatedMapper.java @@ -12,14 +12,16 @@ import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.domain.enums.InvoiceStatus; import dev.vality.newway.domain.tables.pojos.InvoiceCart; +import dev.vality.newway.domain.tables.pojos.InvoiceStatusInfo; import dev.vality.newway.exception.DaoException; -import dev.vality.newway.handler.event.stock.LocalStorage; +import dev.vality.newway.mapper.Mapper; import dev.vality.newway.model.InvoiceWrapper; import dev.vality.newway.util.JsonUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -27,38 +29,48 @@ @Slf4j @Component @RequiredArgsConstructor -public class InvoiceCreatedMapper extends AbstractInvoicingInvoiceMapper { +public class InvoiceCreatedMapper implements Mapper { - private Filter filter = new PathConditionFilter( + private final Filter filter = new PathConditionFilter( new PathConditionRule("invoice_created", new IsNullCondition().not()) ); @Override - public InvoiceWrapper map(InvoiceChange invoiceChange, MachineEvent event, Integer changeId, LocalStorage storage) + public InvoiceWrapper map(InvoiceChange invoiceChange, MachineEvent event, Integer changeId) throws DaoException { Invoice invoice = invoiceChange.getInvoiceCreated().getInvoice(); long sequenceId = event.getEventId(); String invoiceId = event.getSourceId(); - log.info("Start invoice created mapping, sequenceId={}, invoiceId={}, partyId={}, shopId={}", - sequenceId, invoiceId, invoice.getOwnerId(), invoice.getShopId()); + log.info("Start invoice created mapping, sequenceId={}, changeId={}, invoiceId={}, partyId={}, shopId={}", + sequenceId, changeId, invoiceId, invoice.getOwnerId(), invoice.getShopId()); - dev.vality.newway.domain.tables.pojos.Invoice invoiceRecord = - new dev.vality.newway.domain.tables.pojos.Invoice(); - setDefaultProperties(invoiceRecord, sequenceId, changeId, event.getCreatedAt()); + InvoiceWrapper invoiceWrapper = new InvoiceWrapper(); + LocalDateTime eventCreatedAt = TypeUtil.stringToLocalDateTime(event.getCreatedAt()); + invoiceWrapper.setInvoice(getInvoice(invoice, sequenceId, changeId, eventCreatedAt)); + invoiceWrapper.setInvoiceStatusInfo(getInvoiceStatusInfo(invoice, sequenceId, changeId, eventCreatedAt)); + if (invoice.getDetails().isSetCart()) { + invoiceWrapper.setCarts(getInvoiceCarts(invoice, sequenceId, changeId, eventCreatedAt)); + } + log.info("Invoice has been mapped, sequenceId={}, changeId={}, invoiceId={}, partyId={}, shopId={}", + sequenceId, changeId, invoiceId, invoice.getOwnerId(), invoice.getShopId()); + return invoiceWrapper; + } + + private dev.vality.newway.domain.tables.pojos.Invoice getInvoice(Invoice invoice, + Long sequenceId, + Integer changeId, + LocalDateTime eventCreatedAt) { + var invoiceRecord = new dev.vality.newway.domain.tables.pojos.Invoice(); + invoiceRecord.setChangeId(changeId); + invoiceRecord.setSequenceId(sequenceId); + invoiceRecord.setEventCreatedAt(eventCreatedAt); invoiceRecord.setInvoiceId(invoice.getId()); invoiceRecord.setExternalId(invoice.getExternalId()); invoiceRecord.setPartyId(invoice.getOwnerId()); invoiceRecord.setShopId(invoice.getShopId()); invoiceRecord.setPartyRevision(invoice.getPartyRevision()); invoiceRecord.setCreatedAt(TypeUtil.stringToLocalDateTime(invoice.getCreatedAt())); - InvoiceStatus status = TBaseUtil.unionFieldToEnum(invoice.getStatus(), InvoiceStatus.class); - invoiceRecord.setStatus(status); - if (invoice.getStatus().isSetCancelled()) { - invoiceRecord.setStatusCancelledDetails(invoice.getStatus().getCancelled().getDetails()); - } else if (invoice.getStatus().isSetFulfilled()) { - invoiceRecord.setStatusFulfilledDetails(invoice.getStatus().getFulfilled().getDetails()); - } invoiceRecord.setDetailsProduct(invoice.getDetails().getProduct()); invoiceRecord.setDetailsDescription(invoice.getDetails().getDescription()); invoiceRecord.setDue(TypeUtil.stringToLocalDateTime(invoice.getDue())); @@ -67,25 +79,49 @@ public InvoiceWrapper map(InvoiceChange invoiceChange, MachineEvent event, Integ invoiceRecord.setContext(invoice.getContext().getData()); invoiceRecord.setTemplateId(invoice.getTemplateId()); - InvoiceWrapper invoiceWrapper = new InvoiceWrapper(); - invoiceWrapper.setInvoice(invoiceRecord); - if (invoice.getDetails().isSetCart()) { - List invoiceCarts = invoice.getDetails().getCart().getLines().stream().map(il -> { - InvoiceCart ic = new InvoiceCart(); - ic.setProduct(il.getProduct()); - ic.setQuantity(il.getQuantity()); - ic.setAmount(il.getPrice().getAmount()); - ic.setCurrencyCode(il.getPrice().getCurrency().getSymbolicCode()); - Map jsonNodeMap = il.getMetadata().entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, e -> JsonUtil.thriftBaseToJsonNode(e.getValue()))); - ic.setMetadataJson(JsonUtil.objectToJsonString(jsonNodeMap)); - return ic; - }).collect(Collectors.toList()); - invoiceWrapper.setCarts(invoiceCarts); + return invoiceRecord; + } + + private InvoiceStatusInfo getInvoiceStatusInfo(Invoice invoice, + Long sequenceId, + Integer changeId, + LocalDateTime eventCreatedAt) { + InvoiceStatusInfo statusRecord = new InvoiceStatusInfo(); + statusRecord.setInvoiceId(invoice.getId()); + statusRecord.setStatus(TBaseUtil.unionFieldToEnum(invoice.getStatus(), InvoiceStatus.class)); + if (invoice.getStatus().isSetCancelled()) { + statusRecord.setDetails(invoice.getStatus().getCancelled().getDetails()); + } else if (invoice.getStatus().isSetFulfilled()) { + statusRecord.setDetails(invoice.getStatus().getFulfilled().getDetails()); } - log.info("Invoice has been mapped, sequenceId={}, invoiceId={}, partyId={}, shopId={}", - sequenceId, invoiceId, invoice.getOwnerId(), invoice.getShopId()); - return invoiceWrapper; + statusRecord.setEventCreatedAt(eventCreatedAt); + statusRecord.setChangeId(changeId); + statusRecord.setSequenceId(sequenceId); + statusRecord.setExternalId(invoice.getExternalId()); + + return statusRecord; + } + + private List getInvoiceCarts(Invoice invoice, + Long sequenceId, + Integer changeId, + LocalDateTime eventCreatedAt) { + return invoice.getDetails().getCart().getLines().stream().map(il -> { + InvoiceCart ic = new InvoiceCart(); + ic.setEventCreatedAt(eventCreatedAt); + ic.setInvoiceId(invoice.getId()); + ic.setProduct(il.getProduct()); + ic.setQuantity(il.getQuantity()); + ic.setAmount(il.getPrice().getAmount()); + ic.setCurrencyCode(il.getPrice().getCurrency().getSymbolicCode()); + Map jsonNodeMap = il.getMetadata().entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> JsonUtil.thriftBaseToJsonNode(e.getValue()))); + ic.setMetadataJson(JsonUtil.objectToJsonString(jsonNodeMap)); + ic.setSequenceId(sequenceId); + ic.setChangeId(changeId); + return ic; + }) + .collect(Collectors.toList()); } @Override diff --git a/src/main/java/dev/vality/newway/mapper/invoice/InvoiceStatusChangedMapper.java b/src/main/java/dev/vality/newway/mapper/invoice/InvoiceStatusChangedMapper.java index 731267cc..72851590 100644 --- a/src/main/java/dev/vality/newway/mapper/invoice/InvoiceStatusChangedMapper.java +++ b/src/main/java/dev/vality/newway/mapper/invoice/InvoiceStatusChangedMapper.java @@ -3,16 +3,16 @@ import dev.vality.damsel.domain.InvoiceStatus; import dev.vality.damsel.payment_processing.InvoiceChange; import dev.vality.geck.common.util.TBaseUtil; +import dev.vality.geck.common.util.TypeUtil; import dev.vality.geck.filter.Filter; import dev.vality.geck.filter.PathConditionFilter; import dev.vality.geck.filter.condition.IsNullCondition; import dev.vality.geck.filter.rule.PathConditionRule; import dev.vality.machinegun.eventsink.MachineEvent; -import dev.vality.newway.domain.tables.pojos.Invoice; +import dev.vality.newway.domain.tables.pojos.InvoiceStatusInfo; import dev.vality.newway.exception.DaoException; -import dev.vality.newway.handler.event.stock.LocalStorage; +import dev.vality.newway.mapper.Mapper; import dev.vality.newway.model.InvoiceWrapper; -import dev.vality.newway.service.InvoiceWrapperService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -20,40 +20,40 @@ @Slf4j @Component @RequiredArgsConstructor -public class InvoiceStatusChangedMapper extends AbstractInvoicingInvoiceMapper { - - private final InvoiceWrapperService invoiceWrapperService; +public class InvoiceStatusChangedMapper implements Mapper { private Filter filter = new PathConditionFilter( new PathConditionRule("invoice_status_changed", new IsNullCondition().not())); @Override - public InvoiceWrapper map(InvoiceChange invoiceChange, MachineEvent event, Integer changeId, LocalStorage storage) + public InvoiceWrapper map(InvoiceChange invoiceChange, MachineEvent event, Integer changeId) throws DaoException { InvoiceStatus invoiceStatus = invoiceChange.getInvoiceStatusChanged().getStatus(); long sequenceId = event.getEventId(); String invoiceId = event.getSourceId(); - InvoiceWrapper invoiceWrapper = invoiceWrapperService.get(invoiceId, storage); - Invoice invoiceSource = invoiceWrapper.getInvoice(); - log.info("Start invoice status changed mapping, sequenceId={}, invoiceId={}, partyId={}, shopId={}, status={}", - sequenceId, invoiceId, invoiceSource.getPartyId(), invoiceSource.getShopId(), - invoiceStatus.getSetField().getFieldName()); + log.info("Start invoice status changed mapping, sequenceId={}, changeId={}, invoiceId={}, changeId={}, status={}", + sequenceId, changeId, invoiceId, changeId, invoiceStatus.getSetField().getFieldName()); - setDefaultProperties(invoiceSource, sequenceId, changeId, event.getCreatedAt()); - invoiceSource.setStatus( - TBaseUtil.unionFieldToEnum(invoiceStatus, dev.vality.newway.domain.enums.InvoiceStatus.class)); + InvoiceStatusInfo statusRecord = new InvoiceStatusInfo(); + statusRecord.setInvoiceId(invoiceId); + statusRecord.setStatus(TBaseUtil.unionFieldToEnum( + invoiceStatus, + dev.vality.newway.domain.enums.InvoiceStatus.class + )); if (invoiceStatus.isSetCancelled()) { - invoiceSource.setStatusCancelledDetails(invoiceStatus.getCancelled().getDetails()); - invoiceSource.setStatusFulfilledDetails(null); + statusRecord.setDetails(invoiceStatus.getCancelled().getDetails()); } else if (invoiceStatus.isSetFulfilled()) { - invoiceSource.setStatusCancelledDetails(null); - invoiceSource.setStatusFulfilledDetails(invoiceStatus.getFulfilled().getDetails()); + statusRecord.setDetails(invoiceStatus.getFulfilled().getDetails()); } + statusRecord.setEventCreatedAt(TypeUtil.stringToLocalDateTime(event.getCreatedAt())); + statusRecord.setChangeId(changeId); + statusRecord.setSequenceId(sequenceId); - log.info("Invoice has been mapped, sequenceId={}, invoiceId={}, partyId={}, shopId={}, status={}", - sequenceId, invoiceId, invoiceSource.getPartyId(), invoiceSource.getShopId(), - invoiceStatus.getSetField().getFieldName()); + log.info("Invoice has been mapped, sequenceId={}, changeId={}, invoiceId={}, changeId={}, status={}", + sequenceId, changeId, invoiceId, changeId, invoiceStatus.getSetField().getFieldName()); + var invoiceWrapper = new InvoiceWrapper(); + invoiceWrapper.setInvoiceStatusInfo(statusRecord); return invoiceWrapper; } diff --git a/src/main/java/dev/vality/newway/mapper/payment/AbstractInvoicingPaymentMapper.java b/src/main/java/dev/vality/newway/mapper/payment/AbstractInvoicingPaymentMapper.java deleted file mode 100644 index b289eb83..00000000 --- a/src/main/java/dev/vality/newway/mapper/payment/AbstractInvoicingPaymentMapper.java +++ /dev/null @@ -1,32 +0,0 @@ -package dev.vality.newway.mapper.payment; - -import dev.vality.geck.common.util.TypeUtil; -import dev.vality.newway.domain.tables.pojos.Payment; -import dev.vality.newway.mapper.AbstractInvoicingMapper; -import dev.vality.newway.model.PaymentWrapper; - -public abstract class AbstractInvoicingPaymentMapper extends AbstractInvoicingMapper { - - protected void setInsertProperties(Payment payment, Long sequenceId, Integer changeId, String eventCreatedAt) { - payment.setId(null); - payment.setWtime(null); - payment.setChangeId(changeId); - payment.setSequenceId(sequenceId); - payment.setEventCreatedAt(TypeUtil.stringToLocalDateTime(eventCreatedAt)); - payment.setSessionPayloadTransactionBoundTrxExtraJson(null); - payment.setTrxAdditionalInfoAcsUrl(null); - payment.setTrxAdditionalInfoPareq(null); - payment.setTrxAdditionalInfoMd(null); - payment.setTrxAdditionalInfoTermUrl(null); - payment.setTrxAdditionalInfoPares(null); - payment.setTrxAdditionalInfoCavv(null); - payment.setTrxAdditionalInfoXid(null); - payment.setTrxAdditionalInfoCavvAlgorithm(null); - payment.setTrxAdditionalInfoThreeDsVerification(null); - } - - protected void setUpdateProperties(Payment payment, String eventCreatedAt) { - payment.setWtime(null); - payment.setEventCreatedAt(TypeUtil.stringToLocalDateTime(eventCreatedAt)); - } -} diff --git a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCaptureStartedMapper.java b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCaptureStartedMapper.java index 96acf096..2c584552 100644 --- a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCaptureStartedMapper.java +++ b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCaptureStartedMapper.java @@ -1,70 +1,42 @@ package dev.vality.newway.mapper.payment; import dev.vality.damsel.payment_processing.InvoiceChange; -import dev.vality.damsel.payment_processing.InvoicePaymentCaptureData; -import dev.vality.damsel.payment_processing.InvoicePaymentCaptureStarted; -import dev.vality.damsel.payment_processing.InvoicePaymentChange; import dev.vality.geck.filter.Filter; import dev.vality.geck.filter.PathConditionFilter; import dev.vality.geck.filter.condition.IsNullCondition; import dev.vality.geck.filter.rule.PathConditionRule; import dev.vality.machinegun.eventsink.MachineEvent; -import dev.vality.newway.domain.tables.pojos.Payment; -import dev.vality.newway.handler.event.stock.LocalStorage; +import dev.vality.newway.mapper.Mapper; import dev.vality.newway.model.PaymentWrapper; -import dev.vality.newway.service.PaymentWrapperService; -import dev.vality.newway.util.JsonUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import java.util.stream.Collectors; - +/** + * We do not save InvoicePaymentCaptureStarted because it is first commit in two-phase transaction - + * processed -> captured status transition. + * InvoicePaymentCaptureStarted payload duplicates InvoicePaymentStatusChanged event + * with InvoicePaymentCaptured payload. + */ @Slf4j @Component @RequiredArgsConstructor -public class InvoicePaymentCaptureStartedMapper extends AbstractInvoicingPaymentMapper { - - private final PaymentWrapperService paymentWrapperService; +public class InvoicePaymentCaptureStartedMapper implements Mapper { private final Filter filter = new PathConditionFilter(new PathConditionRule( "invoice_payment_change.payload.invoice_payment_capture_started", new IsNullCondition().not())); @Override - public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId, LocalStorage storage) { - InvoicePaymentChange invoicePaymentChange = change.getInvoicePaymentChange(); - - long sequenceId = event.getEventId(); - String paymentId = invoicePaymentChange.getId(); - String invoiceId = event.getSourceId(); - log.info("Start payment capture started handling, sequenceId={}, invoiceId={}, paymentId={}", sequenceId, - invoiceId, paymentId); - - PaymentWrapper paymentWrapper = paymentWrapperService.get(invoiceId, paymentId, sequenceId, changeId, storage); - if (paymentWrapper == null) { - return null; - } - - Payment paymentSource = paymentWrapper.getPayment(); - setUpdateProperties(paymentSource, event.getCreatedAt()); - InvoicePaymentCaptureStarted invoicePaymentCaptureStarted = - change.getInvoicePaymentChange().getPayload().getInvoicePaymentCaptureStarted(); - InvoicePaymentCaptureData data = invoicePaymentCaptureStarted.getData(); - if (data.isSetCash()) { - paymentSource.setAmount(data.getCash().getAmount()); - paymentSource.setCurrencyCode(data.getCash().getCurrency().getSymbolicCode()); - } - paymentSource.setStatusCapturedStartedReason(data.getReason()); - if (data.isSetCart()) { - String cartsJson = JsonUtil.objectToJsonString( - data.getCart().getLines().stream().map(JsonUtil::thriftBaseToJsonNode) - .collect(Collectors.toList())); - paymentSource.setCaptureStartedParamsCartJson(cartsJson); - } - log.info("Payment has been saved, sequenceId={}, invoiceId={}, paymentId={}", sequenceId, invoiceId, - paymentSource.getId()); - return paymentWrapper; + public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId) { + log.info("Receive InvoicePaymentCaptureStarted sequenceId={}, changeId={}, invoiceId={}, paymentId={}, payload={}", + event.getEventId(), + changeId, + change.getInvoicePaymentChange().getId(), + event.getSourceId(), + change.getInvoicePaymentChange().getPayload().getInvoicePaymentCaptureStarted() + ); + return new PaymentWrapper(); } @Override diff --git a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCashFlowChangedMapper.java b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCashFlowChangedMapper.java index 49699088..2472ce3f 100644 --- a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCashFlowChangedMapper.java +++ b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCashFlowChangedMapper.java @@ -3,63 +3,57 @@ import dev.vality.damsel.domain.FinalCashFlowPosting; import dev.vality.damsel.payment_processing.InvoiceChange; import dev.vality.damsel.payment_processing.InvoicePaymentChange; +import dev.vality.geck.common.util.TypeUtil; import dev.vality.geck.filter.Filter; import dev.vality.geck.filter.PathConditionFilter; import dev.vality.geck.filter.condition.IsNullCondition; import dev.vality.geck.filter.rule.PathConditionRule; import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.domain.enums.PaymentChangeType; -import dev.vality.newway.domain.tables.pojos.CashFlow; -import dev.vality.newway.domain.tables.pojos.Payment; -import dev.vality.newway.handler.event.stock.LocalStorage; +import dev.vality.newway.factory.cash.flow.CashFlowFactory; +import dev.vality.newway.mapper.Mapper; +import dev.vality.newway.model.CashFlowWrapper; +import dev.vality.newway.model.InvoicingKey; import dev.vality.newway.model.PaymentWrapper; -import dev.vality.newway.service.PaymentWrapperService; -import dev.vality.newway.util.CashFlowType; -import dev.vality.newway.util.CashFlowUtil; +import dev.vality.newway.factory.cash.flow.CashFlowLinkFactory; +import dev.vality.newway.factory.invoice.payment.PaymentFeeFactory; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import java.time.LocalDateTime; import java.util.List; -import java.util.Map; @Slf4j @Component @RequiredArgsConstructor -public class InvoicePaymentCashFlowChangedMapper extends AbstractInvoicingPaymentMapper { - - private final PaymentWrapperService paymentWrapperService; +public class InvoicePaymentCashFlowChangedMapper implements Mapper { private Filter filter = new PathConditionFilter(new PathConditionRule( "invoice_payment_change.payload.invoice_payment_cash_flow_changed", new IsNullCondition().not())); @Override - public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId, LocalStorage storage) { + public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId) { InvoicePaymentChange invoicePaymentChange = change.getInvoicePaymentChange(); String invoiceId = event.getSourceId(); String paymentId = invoicePaymentChange.getId(); long sequenceId = event.getEventId(); - log.info("Start mapping payment cashflow change, sequenceId='{}', invoiceId='{}', paymentId='{}'", sequenceId, - invoiceId, paymentId); - PaymentWrapper paymentWrapper = paymentWrapperService.get(invoiceId, paymentId, sequenceId, changeId, storage); - if (paymentWrapper == null) { - return null; - } - paymentWrapper.setShouldInsert(true); - Payment paymentSource = paymentWrapper.getPayment(); - setInsertProperties(paymentSource, sequenceId, changeId, event.getCreatedAt()); + LocalDateTime eventCreatedAt = TypeUtil.stringToLocalDateTime(event.getCreatedAt()); + log.info("Start mapping payment cashflow change, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); List finalCashFlow = invoicePaymentChange.getPayload().getInvoicePaymentCashFlowChanged().getCashFlow(); - List cashFlows = CashFlowUtil.convertCashFlows(finalCashFlow, null, PaymentChangeType.payment); - paymentWrapper.setCashFlows(cashFlows); - Map parsedCashFlow = CashFlowUtil.parseCashFlow(finalCashFlow); - paymentSource.setFee(parsedCashFlow.getOrDefault(CashFlowType.FEE, 0L)); - paymentSource.setProviderFee(parsedCashFlow.getOrDefault(CashFlowType.PROVIDER_FEE, 0L)); - paymentSource.setExternalFee(parsedCashFlow.getOrDefault(CashFlowType.EXTERNAL_FEE, 0L)); - paymentSource.setGuaranteeDeposit(parsedCashFlow.getOrDefault(CashFlowType.GUARANTEE_DEPOSIT, 0L)); - log.info("Payment cashflow has been mapped, sequenceId='{}', invoiceId='{}', paymentId='{}'", sequenceId, - invoiceId, paymentId); + PaymentWrapper paymentWrapper = new PaymentWrapper(); + paymentWrapper.setKey(InvoicingKey.buildKey(invoiceId, paymentId)); + paymentWrapper.setCashFlowWrapper(new CashFlowWrapper( + CashFlowLinkFactory.build(paymentId, invoiceId, eventCreatedAt, changeId, sequenceId), + CashFlowFactory.build(finalCashFlow, null, PaymentChangeType.payment) + )); + paymentWrapper.setPaymentFee( + PaymentFeeFactory.build(finalCashFlow, invoiceId, paymentId, eventCreatedAt, changeId, sequenceId)); + log.info("Payment cashflow has been mapped, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); return paymentWrapper; } @@ -67,4 +61,5 @@ public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer chan public Filter getFilter() { return filter; } + } diff --git a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCreatedMapper.java b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCreatedMapper.java index f27dff49..c010f01c 100644 --- a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCreatedMapper.java +++ b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCreatedMapper.java @@ -11,174 +11,223 @@ import dev.vality.geck.filter.rule.PathConditionRule; import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.domain.enums.*; -import dev.vality.newway.domain.tables.pojos.CashFlow; -import dev.vality.newway.domain.tables.pojos.Invoice; import dev.vality.newway.domain.tables.pojos.Payment; -import dev.vality.newway.handler.event.stock.LocalStorage; -import dev.vality.newway.model.InvoiceWrapper; +import dev.vality.newway.domain.tables.pojos.PaymentPayerInfo; +import dev.vality.newway.factory.cash.flow.CashFlowFactory; +import dev.vality.newway.factory.cash.flow.CashFlowLinkFactory; +import dev.vality.newway.factory.invoice.payment.*; +import dev.vality.newway.mapper.Mapper; +import dev.vality.newway.model.CashFlowWrapper; import dev.vality.newway.model.InvoicingKey; +import dev.vality.newway.model.PartyShop; import dev.vality.newway.model.PaymentWrapper; -import dev.vality.newway.service.InvoiceWrapperService; -import dev.vality.newway.util.CashFlowType; -import dev.vality.newway.util.CashFlowUtil; -import dev.vality.newway.util.JsonUtil; +import dev.vality.newway.service.PartyShopCacheService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import java.util.List; -import java.util.Map; +import java.time.LocalDateTime; import java.util.Optional; @Slf4j @Component @RequiredArgsConstructor -public class InvoicePaymentCreatedMapper extends AbstractInvoicingPaymentMapper { +public class InvoicePaymentCreatedMapper implements Mapper { - private final InvoiceWrapperService invoiceWrapperService; + private final PartyShopCacheService partyShopCacheService; private Filter filter = new PathConditionFilter(new PathConditionRule( "invoice_payment_change.payload.invoice_payment_started", new IsNullCondition().not())); @Override - public PaymentWrapper map(InvoiceChange invoiceChange, MachineEvent event, Integer changeId, LocalStorage storage) { + public PaymentWrapper map(InvoiceChange invoiceChange, MachineEvent event, Integer changeId) { InvoicePaymentStarted invoicePaymentStarted = invoiceChange .getInvoicePaymentChange() .getPayload() .getInvoicePaymentStarted(); - PaymentWrapper paymentWrapper = new PaymentWrapper(); - paymentWrapper.setShouldInsert(true); - Payment payment = new Payment(); - paymentWrapper.setPayment(payment); InvoicePayment invoicePayment = invoicePaymentStarted.getPayment(); long sequenceId = event.getEventId(); String invoiceId = event.getSourceId(); - String paymentId = invoicePayment.getId(); - log.info("Start payment created mapping, sequenceId={}, invoiceId={}, paymentId={}", sequenceId, invoiceId, - paymentId); - paymentWrapper.setKey(InvoicingKey.buildKey(invoiceId, paymentId)); - setInsertProperties(payment, sequenceId, changeId, event.getCreatedAt()); - payment.setPaymentId(paymentId); - payment.setCreatedAt(TypeUtil.stringToLocalDateTime(invoicePayment.getCreatedAt())); - payment.setInvoiceId(invoiceId); - payment.setExternalId(invoicePayment.getExternalId()); + log.info("Start payment created mapping, sequenceId={}, changeId={}, invoiceId={}, paymentId={}", + sequenceId, changeId, invoiceId, paymentId); - InvoiceWrapper invoiceWrapper = invoiceWrapperService.get(invoiceId, storage); - if (invoiceWrapper == null) { + PartyShop partyShop = partyShopCacheService.get(invoiceId); + if (partyShop == null) { + log.info("PartyShop not found for invoiceId = {}", invoiceId); return null; } - Invoice invoice = invoiceWrapper.getInvoice(); + PaymentWrapper paymentWrapper = new PaymentWrapper(); + paymentWrapper.setKey(InvoicingKey.buildKey(invoiceId, paymentId)); + LocalDateTime eventCreatedAt = TypeUtil.stringToLocalDateTime(event.getCreatedAt()); + paymentWrapper.setPayment(getPayment( + invoicePayment, + invoiceId, + paymentId, + partyShop, + eventCreatedAt, + changeId, + sequenceId + )); + paymentWrapper.setPaymentStatusInfo(PaymentStatusInfoFactory.build( + invoicePayment.getStatus(), + invoiceId, + paymentId, + eventCreatedAt, + changeId, + sequenceId + )); + paymentWrapper.setPaymentPayerInfo(getPayerPaymentInfo( + invoicePayment, + invoiceId, + paymentId, + eventCreatedAt, + changeId, + sequenceId + )); + if (invoicePaymentStarted.isSetRoute()) { + paymentWrapper.setPaymentRoute(PaymentRouteFactory.build( + invoicePaymentStarted.getRoute(), + invoiceId, + paymentId, + TypeUtil.stringToLocalDateTime(event.getCreatedAt()), + changeId, + sequenceId + )); + } + if (invoicePaymentStarted.isSetCashFlow()) { + paymentWrapper.setCashFlowWrapper(new CashFlowWrapper( + CashFlowLinkFactory.build(paymentId, invoiceId, eventCreatedAt, changeId, sequenceId), + CashFlowFactory.build(invoicePaymentStarted.getCashFlow(), null, PaymentChangeType.payment) + )); + paymentWrapper.setPaymentFee(PaymentFeeFactory.build( + invoicePaymentStarted.getCashFlow(), + invoiceId, + paymentId, + eventCreatedAt, + changeId, + sequenceId + )); + } + log.info("Payment has been mapped, sequenceId={}, changeId={}, invoiceId={}, paymentId={}", + sequenceId, changeId, invoiceId, paymentId); + return paymentWrapper; + } - payment.setPartyId(invoice.getPartyId()); - payment.setShopId(invoice.getShopId()); + private Payment getPayment(InvoicePayment invoicePayment, + String invoiceId, + String paymentId, + PartyShop partyShop, + LocalDateTime eventCreatedAt, + Integer changeId, + Long sequenceId) { + Payment payment = new Payment(); + payment.setEventCreatedAt(eventCreatedAt); + payment.setPaymentId(paymentId); + payment.setInvoiceId(invoiceId); + payment.setCreatedAt(TypeUtil.stringToLocalDateTime(invoicePayment.getCreatedAt())); + payment.setPartyId(partyShop.getPartyId()); + payment.setShopId(partyShop.getShopId()); payment.setDomainRevision(invoicePayment.getDomainRevision()); if (invoicePayment.isSetPartyRevision()) { payment.setPartyRevision(invoicePayment.getPartyRevision()); } - payment.setStatus(TBaseUtil.unionFieldToEnum(invoicePayment.getStatus(), PaymentStatus.class)); - if (invoicePayment.getStatus().isSetCancelled()) { - payment.setStatusCancelledReason(invoicePayment.getStatus().getCancelled().getReason()); - } else if (invoicePayment.getStatus().isSetCaptured()) { - payment.setStatusCapturedReason(invoicePayment.getStatus().getCaptured().getReason()); - } else if (invoicePayment.getStatus().isSetFailed()) { - payment.setStatusFailedFailure(JsonUtil.thriftBaseToJsonString(invoicePayment.getStatus().getFailed())); - } payment.setAmount(invoicePayment.getCost().getAmount()); payment.setCurrencyCode(invoicePayment.getCost().getCurrency().getSymbolicCode()); - Payer payer = invoicePayment.getPayer(); - payment.setPayerType(TBaseUtil.unionFieldToEnum(payer, PayerType.class)); - if (payer.isSetPaymentResource()) { - PaymentResourcePayer paymentResource = payer.getPaymentResource(); - fillPaymentTool(payment, paymentResource.getResource().getPaymentTool()); - fillContactInfo(payment, paymentResource.getContactInfo()); - if (paymentResource.getResource().isSetClientInfo()) { - payment.setPayerIpAddress(paymentResource.getResource().getClientInfo().getIpAddress()); - payment.setPayerFingerprint(paymentResource.getResource().getClientInfo().getFingerprint()); - } - } else if (payer.isSetCustomer()) { - CustomerPayer customer = payer.getCustomer(); - payment.setPayerCustomerId(customer.getCustomerId()); - payment.setPayerCustomerBindingId(customer.getCustomerBindingId()); - payment.setPayerCustomerRecPaymentToolId(customer.getRecPaymentToolId()); - fillPaymentTool(payment, customer.getPaymentTool()); - fillContactInfo(payment, customer.getContactInfo()); - } else if (payer.isSetRecurrent()) { - payment.setPayerRecurrentParentInvoiceId(payer.getRecurrent().getRecurrentParent().getInvoiceId()); - payment.setPayerRecurrentParentPaymentId(payer.getRecurrent().getRecurrentParent().getPaymentId()); - fillPaymentTool(payment, payer.getRecurrent().getPaymentTool()); - fillContactInfo(payment, payer.getRecurrent().getContactInfo()); + if (invoicePayment.isSetMakeRecurrent()) { + payment.setMakeRecurrent(invoicePayment.isMakeRecurrent()); } + payment.setExternalId(invoicePayment.getExternalId()); payment.setPaymentFlowType(TBaseUtil.unionFieldToEnum(invoicePayment.getFlow(), PaymentFlowType.class)); if (invoicePayment.getFlow().isSetHold()) { payment.setPaymentFlowHeldUntil( TypeUtil.stringToLocalDateTime(invoicePayment.getFlow().getHold().getHeldUntil())); payment.setPaymentFlowOnHoldExpiration(invoicePayment.getFlow().getHold().getOnHoldExpiration().name()); } - if (invoicePaymentStarted.isSetRoute()) { - payment.setRouteProviderId(invoicePaymentStarted.getRoute().getProvider().getId()); - payment.setRouteTerminalId(invoicePaymentStarted.getRoute().getTerminal().getId()); - } - if (invoicePayment.isSetMakeRecurrent()) { - payment.setMakeRecurrent(invoicePayment.isMakeRecurrent()); - } + payment.setSequenceId(sequenceId); + payment.setChangeId(changeId); + return payment; + } - if (invoicePaymentStarted.isSetCashFlow()) { - List cashFlowList = - CashFlowUtil.convertCashFlows(invoicePaymentStarted.getCashFlow(), null, PaymentChangeType.payment); - paymentWrapper.setCashFlows(cashFlowList); - Map parsedCashFlow = CashFlowUtil.parseCashFlow(invoicePaymentStarted.getCashFlow()); - payment.setFee(parsedCashFlow.getOrDefault(CashFlowType.FEE, 0L)); - payment.setProviderFee(parsedCashFlow.getOrDefault(CashFlowType.PROVIDER_FEE, 0L)); - payment.setExternalFee(parsedCashFlow.getOrDefault(CashFlowType.EXTERNAL_FEE, 0L)); - payment.setGuaranteeDeposit(parsedCashFlow.getOrDefault(CashFlowType.GUARANTEE_DEPOSIT, 0L)); + private PaymentPayerInfo getPayerPaymentInfo(InvoicePayment invoicePayment, + String invoiceId, + String paymentId, + LocalDateTime eventCreatedAt, + Integer changeId, + Long sequenceId) { + Payer payer = invoicePayment.getPayer(); + PaymentPayerInfo payerInfo = new PaymentPayerInfo(); + payerInfo.setInvoiceId(invoiceId); + payerInfo.setPaymentId(paymentId); + payerInfo.setEventCreatedAt(eventCreatedAt); + payerInfo.setPayerType(TBaseUtil.unionFieldToEnum(payer, PayerType.class)); + if (payer.isSetPaymentResource()) { + PaymentResourcePayer paymentResource = payer.getPaymentResource(); + fillPaymentTool(payerInfo, paymentResource.getResource().getPaymentTool()); + fillContactInfo(payerInfo, paymentResource.getContactInfo()); + if (paymentResource.getResource().isSetClientInfo()) { + payerInfo.setIpAddress(paymentResource.getResource().getClientInfo().getIpAddress()); + payerInfo.setFingerprint(paymentResource.getResource().getClientInfo().getFingerprint()); + } + } else if (payer.isSetCustomer()) { + CustomerPayer customer = payer.getCustomer(); + payerInfo.setCustomerId(customer.getCustomerId()); + payerInfo.setCustomerBindingId(customer.getCustomerBindingId()); + payerInfo.setCustomerRecPaymentToolId(customer.getRecPaymentToolId()); + fillPaymentTool(payerInfo, customer.getPaymentTool()); + fillContactInfo(payerInfo, customer.getContactInfo()); + } else if (payer.isSetRecurrent()) { + payerInfo.setRecurrentParentInvoiceId(payer.getRecurrent().getRecurrentParent().getInvoiceId()); + payerInfo.setRecurrentParentPaymentId(payer.getRecurrent().getRecurrentParent().getPaymentId()); + fillPaymentTool(payerInfo, payer.getRecurrent().getPaymentTool()); + fillContactInfo(payerInfo, payer.getRecurrent().getContactInfo()); } - log.info("Payment has been mapped, sequenceId={}, invoiceId={}, paymentId={}", sequenceId, invoiceId, - paymentId); - return paymentWrapper; + payerInfo.setSequenceId(sequenceId); + payerInfo.setChangeId(changeId); + return payerInfo; } - private void fillContactInfo(Payment payment, ContactInfo contactInfo) { - payment.setPayerPhoneNumber(contactInfo.getPhoneNumber()); - payment.setPayerEmail(contactInfo.getEmail()); + private void fillContactInfo(PaymentPayerInfo payerInfo, ContactInfo contactInfo) { + payerInfo.setPhoneNumber(contactInfo.getPhoneNumber()); + payerInfo.setEmail(contactInfo.getEmail()); } - private void fillPaymentTool(Payment payment, PaymentTool paymentTool) { - payment.setPayerPaymentToolType(TBaseUtil.unionFieldToEnum(paymentTool, PaymentToolType.class)); + private void fillPaymentTool(PaymentPayerInfo payerInfo, PaymentTool paymentTool) { + payerInfo.setPaymentToolType(TBaseUtil.unionFieldToEnum(paymentTool, PaymentToolType.class)); if (paymentTool.isSetBankCard()) { BankCard bankCard = paymentTool.getBankCard(); - payment.setPayerBankCardToken(bankCard.getToken()); - payment.setPayerBankCardPaymentSystem(Optional.ofNullable(bankCard.getPaymentSystem()) + payerInfo.setBankCardToken(bankCard.getToken()); + payerInfo.setBankCardPaymentSystem(Optional.ofNullable(bankCard.getPaymentSystem()) .map(PaymentSystemRef::getId).orElse(null)); - payment.setPayerBankCardBin(bankCard.getBin()); - payment.setPayerBankCardMaskedPan(bankCard.getLastDigits()); - payment.setPayerBankName(bankCard.getBankName()); - payment.setPayerBankCardCardholderName(bankCard.getCardholderName()); + payerInfo.setBankCardBin(bankCard.getBin()); + payerInfo.setBankCardMaskedPan(bankCard.getLastDigits()); + payerInfo.setBankName(bankCard.getBankName()); + payerInfo.setBankCardCardholderName(bankCard.getCardholderName()); if (bankCard.isSetIssuerCountry()) { - payment.setPayerIssuerCountry(bankCard.getIssuerCountry().name()); + payerInfo.setIssuerCountry(bankCard.getIssuerCountry().name()); } - payment.setPayerBankCardTokenProvider(Optional.ofNullable(bankCard.getPaymentToken()) + payerInfo.setBankCardTokenProvider(Optional.ofNullable(bankCard.getPaymentToken()) .map(BankCardTokenServiceRef::getId).orElse(null)); } else if (paymentTool.isSetPaymentTerminal()) { - payment.setPayerPaymentTerminalType( + payerInfo.setPaymentTerminalType( Optional.ofNullable(paymentTool.getPaymentTerminal().getPaymentService()) .map(PaymentServiceRef::getId).orElse(null)); } else if (paymentTool.isSetDigitalWallet()) { - payment.setPayerDigitalWalletId(paymentTool.getDigitalWallet().getId()); - payment.setPayerDigitalWalletProvider( + payerInfo.setDigitalWalletId(paymentTool.getDigitalWallet().getId()); + payerInfo.setDigitalWalletProvider( Optional.ofNullable(paymentTool.getDigitalWallet().getPaymentService()) .map(PaymentServiceRef::getId).orElse(null)); } else if (paymentTool.isSetCryptoCurrency()) { - payment.setPayerCryptoCurrencyType(Optional.ofNullable(paymentTool.getCryptoCurrency()) + payerInfo.setCryptoCurrencyType(Optional.ofNullable(paymentTool.getCryptoCurrency()) .map(CryptoCurrencyRef::getId).orElse(null)); } else if (paymentTool.isSetMobileCommerce()) { - payment.setPayerMobileOperator(paymentTool.getMobileCommerce().getOperator().getId()); - payment.setPayerMobilePhoneCc(paymentTool.getMobileCommerce().getPhone().getCc()); - payment.setPayerMobilePhoneCtn(paymentTool.getMobileCommerce().getPhone().getCtn()); + payerInfo.setMobileOperator(paymentTool.getMobileCommerce().getOperator().getId()); + payerInfo.setMobilePhoneCc(paymentTool.getMobileCommerce().getPhone().getCc()); + payerInfo.setMobilePhoneCtn(paymentTool.getMobileCommerce().getPhone().getCtn()); } } diff --git a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentRecTokenAcquiredMapper.java b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentRecTokenAcquiredMapper.java index 924dc939..99c621be 100644 --- a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentRecTokenAcquiredMapper.java +++ b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentRecTokenAcquiredMapper.java @@ -2,15 +2,16 @@ import dev.vality.damsel.payment_processing.InvoiceChange; import dev.vality.damsel.payment_processing.InvoicePaymentChange; +import dev.vality.geck.common.util.TypeUtil; import dev.vality.geck.filter.Filter; import dev.vality.geck.filter.PathConditionFilter; import dev.vality.geck.filter.condition.IsNullCondition; import dev.vality.geck.filter.rule.PathConditionRule; import dev.vality.machinegun.eventsink.MachineEvent; -import dev.vality.newway.domain.tables.pojos.Payment; -import dev.vality.newway.handler.event.stock.LocalStorage; +import dev.vality.newway.domain.tables.pojos.PaymentRecurrentInfo; +import dev.vality.newway.mapper.Mapper; +import dev.vality.newway.model.InvoicingKey; import dev.vality.newway.model.PaymentWrapper; -import dev.vality.newway.service.PaymentWrapperService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -18,32 +19,32 @@ @Slf4j @Component @RequiredArgsConstructor -public class InvoicePaymentRecTokenAcquiredMapper extends AbstractInvoicingPaymentMapper { - - private final PaymentWrapperService paymentWrapperService; +public class InvoicePaymentRecTokenAcquiredMapper implements Mapper { private Filter filter = new PathConditionFilter(new PathConditionRule( "invoice_payment_change.payload.invoice_payment_rec_token_acquired", new IsNullCondition().not())); @Override - public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId, LocalStorage storage) { + public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId) { InvoicePaymentChange invoicePaymentChange = change.getInvoicePaymentChange(); String invoiceId = event.getSourceId(); String paymentId = invoicePaymentChange.getId(); long sequenceId = event.getEventId(); - log.info("Start handling payment recurrent token acquired, sequenceId='{}', invoiceId='{}', paymentId='{}'", - sequenceId, invoiceId, paymentId); - PaymentWrapper paymentWrapper = paymentWrapperService.get(invoiceId, paymentId, sequenceId, changeId, storage); - if (paymentWrapper == null) { - return null; - } - Payment paymentSource = paymentWrapper.getPayment(); - setUpdateProperties(paymentSource, event.getCreatedAt()); - String token = invoicePaymentChange.getPayload().getInvoicePaymentRecTokenAcquired().getToken(); - paymentSource.setRecurrentIntentionToken(token); - log.info("Payment recurrent token have been saved, sequenceId='{}', invoiceId='{}', paymentId='{}'", sequenceId, - invoiceId, paymentId); + log.info("Start handling payment recurrent token acquired, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); + PaymentRecurrentInfo recurrentInfo = new PaymentRecurrentInfo(); + recurrentInfo.setEventCreatedAt(TypeUtil.stringToLocalDateTime(event.getCreatedAt())); + recurrentInfo.setInvoiceId(invoiceId); + recurrentInfo.setPaymentId(paymentId); + recurrentInfo.setSequenceId(sequenceId); + recurrentInfo.setChangeId(changeId); + recurrentInfo.setToken(invoicePaymentChange.getPayload().getInvoicePaymentRecTokenAcquired().getToken()); + log.info("Payment recurrent token have been saved, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); + PaymentWrapper paymentWrapper = new PaymentWrapper(); + paymentWrapper.setKey(InvoicingKey.buildKey(invoiceId, paymentId)); + paymentWrapper.setPaymentRecurrentInfo(recurrentInfo); return paymentWrapper; } diff --git a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentRiskScoreChangedMapper.java b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentRiskScoreChangedMapper.java index f4042e71..ec15befb 100644 --- a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentRiskScoreChangedMapper.java +++ b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentRiskScoreChangedMapper.java @@ -9,10 +9,10 @@ import dev.vality.geck.filter.condition.IsNullCondition; import dev.vality.geck.filter.rule.PathConditionRule; import dev.vality.machinegun.eventsink.MachineEvent; -import dev.vality.newway.domain.tables.pojos.Payment; -import dev.vality.newway.handler.event.stock.LocalStorage; +import dev.vality.newway.domain.tables.pojos.PaymentRiskData; +import dev.vality.newway.mapper.Mapper; +import dev.vality.newway.model.InvoicingKey; import dev.vality.newway.model.PaymentWrapper; -import dev.vality.newway.service.PaymentWrapperService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -20,37 +20,38 @@ @Slf4j @Component @RequiredArgsConstructor -public class InvoicePaymentRiskScoreChangedMapper extends AbstractInvoicingPaymentMapper { - - private final PaymentWrapperService paymentWrapperService; +public class InvoicePaymentRiskScoreChangedMapper implements Mapper { private Filter filter = new PathConditionFilter(new PathConditionRule( "invoice_payment_change.payload.invoice_payment_risk_score_changed", new IsNullCondition().not())); @Override - public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId, LocalStorage storage) { + public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId) { InvoicePaymentChange invoicePaymentChange = change.getInvoicePaymentChange(); String invoiceId = event.getSourceId(); String paymentId = invoicePaymentChange.getId(); long sequenceId = event.getEventId(); - log.info("Start mapping payment risk score change, sequenceId='{}', invoiceId='{}', paymentId='{}'", sequenceId, - invoiceId, paymentId); - PaymentWrapper paymentWrapper = paymentWrapperService.get(invoiceId, paymentId, sequenceId, changeId, storage); - if (paymentWrapper == null) { - return null; - } - Payment paymentSource = paymentWrapper.getPayment(); - setUpdateProperties(paymentSource, event.getCreatedAt()); + log.info("Start mapping payment risk score change, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); RiskScore riskScore = invoicePaymentChange.getPayload().getInvoicePaymentRiskScoreChanged().getRiskScore(); dev.vality.newway.domain.enums.RiskScore score = TypeUtil.toEnumField(riskScore.name(), dev.vality.newway.domain.enums.RiskScore.class); if (score == null) { throw new IllegalArgumentException("Illegal risk score: " + riskScore); } - paymentSource.setRiskScore(score); - log.info("Payment risk score have been mapped, sequenceId='{}', invoiceId='{}', paymentId='{}'", sequenceId, - invoiceId, paymentId); + PaymentRiskData paymentRiskData = new PaymentRiskData(); + paymentRiskData.setEventCreatedAt(TypeUtil.stringToLocalDateTime(event.getCreatedAt())); + paymentRiskData.setInvoiceId(invoiceId); + paymentRiskData.setPaymentId(paymentId); + paymentRiskData.setRiskScore(score); + paymentRiskData.setSequenceId(sequenceId); + paymentRiskData.setChangeId(changeId); + log.info("Payment risk score have been mapped, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); + PaymentWrapper paymentWrapper = new PaymentWrapper(); + paymentWrapper.setKey(InvoicingKey.buildKey(invoiceId, paymentId)); + paymentWrapper.setPaymentRiskData(paymentRiskData); return paymentWrapper; } diff --git a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentRouteChangedMapper.java b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentRouteChangedMapper.java index 31fd877b..51bf3b2e 100644 --- a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentRouteChangedMapper.java +++ b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentRouteChangedMapper.java @@ -1,17 +1,18 @@ package dev.vality.newway.mapper.payment; -import dev.vality.damsel.domain.PaymentRoute; import dev.vality.damsel.payment_processing.InvoiceChange; import dev.vality.damsel.payment_processing.InvoicePaymentChange; +import dev.vality.geck.common.util.TypeUtil; import dev.vality.geck.filter.Filter; import dev.vality.geck.filter.PathConditionFilter; import dev.vality.geck.filter.condition.IsNullCondition; import dev.vality.geck.filter.rule.PathConditionRule; import dev.vality.machinegun.eventsink.MachineEvent; -import dev.vality.newway.domain.tables.pojos.Payment; -import dev.vality.newway.handler.event.stock.LocalStorage; +import dev.vality.newway.domain.tables.pojos.PaymentRoute; +import dev.vality.newway.mapper.Mapper; +import dev.vality.newway.model.InvoicingKey; import dev.vality.newway.model.PaymentWrapper; -import dev.vality.newway.service.PaymentWrapperService; +import dev.vality.newway.factory.invoice.payment.PaymentRouteFactory; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -19,33 +20,35 @@ @Slf4j @Component @RequiredArgsConstructor -public class InvoicePaymentRouteChangedMapper extends AbstractInvoicingPaymentMapper { - - private final PaymentWrapperService paymentWrapperService; +public class InvoicePaymentRouteChangedMapper implements Mapper { private Filter filter = new PathConditionFilter(new PathConditionRule( "invoice_payment_change.payload.invoice_payment_route_changed", new IsNullCondition().not())); @Override - public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId, LocalStorage storage) { + public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId) { InvoicePaymentChange invoicePaymentChange = change.getInvoicePaymentChange(); String invoiceId = event.getSourceId(); String paymentId = invoicePaymentChange.getId(); - PaymentRoute paymentRoute = invoicePaymentChange.getPayload().getInvoicePaymentRouteChanged().getRoute(); + dev.vality.damsel.domain.PaymentRoute paymentRouteSource = + invoicePaymentChange.getPayload().getInvoicePaymentRouteChanged().getRoute(); long sequenceId = event.getEventId(); - log.info("Start mapping payment route change, route='{}', sequenceId='{}', invoiceId='{}', paymentId='{}'", - paymentRoute, sequenceId, invoiceId, paymentId); - PaymentWrapper paymentWrapper = paymentWrapperService.get(invoiceId, paymentId, sequenceId, changeId, storage); - if (paymentWrapper == null) { - return null; - } - Payment paymentSource = paymentWrapper.getPayment(); - setUpdateProperties(paymentSource, event.getCreatedAt()); - paymentSource.setRouteProviderId(paymentRoute.getProvider().getId()); - paymentSource.setRouteTerminalId(paymentRoute.getTerminal().getId()); - log.info("Payment route have been mapped, route='{}', sequenceId='{}', invoiceId='{}', paymentId='{}'", - paymentRoute, sequenceId, invoiceId, paymentId); + log.info("Start mapping payment route change, route='{}', sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + paymentRouteSource, sequenceId, changeId, invoiceId, paymentId); + PaymentRoute paymentRoute = PaymentRouteFactory.build( + paymentRouteSource, + invoiceId, + paymentId, + TypeUtil.stringToLocalDateTime(event.getCreatedAt()), + changeId, + sequenceId + ); + log.info("Payment route have been mapped, route='{}', sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + paymentRoute, sequenceId, changeId, invoiceId, paymentId); + PaymentWrapper paymentWrapper = new PaymentWrapper(); + paymentWrapper.setKey(InvoicingKey.buildKey(invoiceId, paymentId)); + paymentWrapper.setPaymentRoute(paymentRoute); return paymentWrapper; } diff --git a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentSessionChangeTransactionBoundMapper.java b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentSessionChangeTransactionBoundMapper.java index 99aec1a0..24b4d393 100644 --- a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentSessionChangeTransactionBoundMapper.java +++ b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentSessionChangeTransactionBoundMapper.java @@ -6,15 +6,16 @@ import dev.vality.damsel.payment_processing.InvoicePaymentChange; import dev.vality.damsel.payment_processing.InvoicePaymentSessionChange; import dev.vality.damsel.payment_processing.SessionChangePayload; +import dev.vality.geck.common.util.TypeUtil; import dev.vality.geck.filter.Filter; import dev.vality.geck.filter.PathConditionFilter; import dev.vality.geck.filter.condition.IsNullCondition; import dev.vality.geck.filter.rule.PathConditionRule; import dev.vality.machinegun.eventsink.MachineEvent; -import dev.vality.newway.domain.tables.pojos.Payment; -import dev.vality.newway.handler.event.stock.LocalStorage; +import dev.vality.newway.domain.tables.pojos.PaymentAdditionalInfo; +import dev.vality.newway.mapper.Mapper; +import dev.vality.newway.model.InvoicingKey; import dev.vality.newway.model.PaymentWrapper; -import dev.vality.newway.service.PaymentWrapperService; import dev.vality.newway.util.JsonUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -25,60 +26,54 @@ @Slf4j @Component @RequiredArgsConstructor -public class InvoicePaymentSessionChangeTransactionBoundMapper extends AbstractInvoicingPaymentMapper { - - private final PaymentWrapperService paymentWrapperService; +public class InvoicePaymentSessionChangeTransactionBoundMapper implements Mapper { private Filter filter = new PathConditionFilter(new PathConditionRule( "invoice_payment_change.payload.invoice_payment_session_change.payload.session_transaction_bound", new IsNullCondition().not())); @Override - public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId, LocalStorage storage) { + public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId) { InvoicePaymentChange invoicePaymentChange = change.getInvoicePaymentChange(); String invoiceId = event.getSourceId(); String paymentId = invoicePaymentChange.getId(); long sequenceId = event.getEventId(); - log.info("Start mapping session change transaction info, sequenceId='{}', invoiceId='{}', paymentId='{}'", - sequenceId, invoiceId, paymentId); - - PaymentWrapper paymentWrapper = paymentWrapperService.get(invoiceId, paymentId, sequenceId, changeId, storage); - if (paymentWrapper == null) { - return null; - } - Payment paymentSource = paymentWrapper.getPayment(); - setUpdateProperties(paymentSource, event.getCreatedAt()); + log.info("Start mapping session change transaction info, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); InvoicePaymentSessionChange sessionChange = invoicePaymentChange.getPayload().getInvoicePaymentSessionChange(); SessionChangePayload payload = sessionChange.getPayload(); TransactionInfo transactionInfo = payload.getSessionTransactionBound().getTrx(); - paymentSource.setSessionPayloadTransactionBoundTrxId(transactionInfo.getId()); - Map extra = transactionInfo.getExtra(); - if (extra.get("PaRes") != null) { - extra.put("PaRes", null); - } - paymentSource.setSessionPayloadTransactionBoundTrxExtraJson(JsonUtil.objectToJsonString(extra)); + PaymentAdditionalInfo additionalInfo = new PaymentAdditionalInfo(); + additionalInfo.setEventCreatedAt(TypeUtil.stringToLocalDateTime(event.getCreatedAt())); + additionalInfo.setInvoiceId(invoiceId); + additionalInfo.setPaymentId(paymentId); + additionalInfo.setTransactionId(transactionInfo.getId()); + Map extra = transactionInfo.getExtra(); + extra.replace("PaRes", null); + additionalInfo.setExtraJson(JsonUtil.objectToJsonString(extra)); if (transactionInfo.isSetAdditionalInfo()) { - AdditionalTransactionInfo additionalTransactionInfo = transactionInfo.getAdditionalInfo(); - paymentSource.setTrxAdditionalInfoRrn(additionalTransactionInfo.getRrn()); - paymentSource.setTrxAdditionalInfoApprovalCode(additionalTransactionInfo.getApprovalCode()); - paymentSource.setTrxAdditionalInfoAcsUrl(additionalTransactionInfo.getAcsUrl()); - //paymentSource.setTrxAdditionalInfoPareq(additionalTransactionInfo.getPareq()); - paymentSource.setTrxAdditionalInfoMd(additionalTransactionInfo.getMd()); - paymentSource.setTrxAdditionalInfoTermUrl(additionalTransactionInfo.getTermUrl()); - //paymentSource.setTrxAdditionalInfoPares(additionalTransactionInfo.getPares()); - paymentSource.setTrxAdditionalInfoEci(additionalTransactionInfo.getEci()); - paymentSource.setTrxAdditionalInfoCavv(additionalTransactionInfo.getCavv()); - paymentSource.setTrxAdditionalInfoXid(additionalTransactionInfo.getXid()); - paymentSource.setTrxAdditionalInfoCavvAlgorithm(additionalTransactionInfo.getCavvAlgorithm()); - - if (additionalTransactionInfo.isSetThreeDsVerification()) { - paymentSource.setTrxAdditionalInfoThreeDsVerification( - additionalTransactionInfo.getThreeDsVerification().name()); + AdditionalTransactionInfo additionalInfoSource = transactionInfo.getAdditionalInfo(); + additionalInfo.setRrn(additionalInfoSource.getRrn()); + additionalInfo.setApprovalCode(additionalInfoSource.getApprovalCode()); + additionalInfo.setAcsUrl(additionalInfoSource.getAcsUrl()); + additionalInfo.setMd(additionalInfoSource.getMd()); + additionalInfo.setTermUrl(additionalInfoSource.getTermUrl()); + additionalInfo.setEci(additionalInfoSource.getEci()); + additionalInfo.setCavv(additionalInfoSource.getCavv()); + additionalInfo.setXid(additionalInfoSource.getXid()); + additionalInfo.setCavvAlgorithm(additionalInfoSource.getCavvAlgorithm()); + if (additionalInfoSource.isSetThreeDsVerification()) { + additionalInfo.setThreeDsVerification(additionalInfoSource.getThreeDsVerification().name()); } } - log.info("Payment session transaction info has been mapped, sequenceId='{}', invoiceId='{}', paymentId='{}'", - sequenceId, invoiceId, paymentId); + additionalInfo.setSequenceId(sequenceId); + additionalInfo.setChangeId(changeId); + log.info("Payment session transaction info has been mapped, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); + PaymentWrapper paymentWrapper = new PaymentWrapper(); + paymentWrapper.setKey(InvoicingKey.buildKey(invoiceId, paymentId)); + paymentWrapper.setPaymentAdditionalInfo(additionalInfo); return paymentWrapper; } diff --git a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentStatusChangedMapper.java b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentStatusChangedMapper.java index 0b8cbea5..c29015a4 100644 --- a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentStatusChangedMapper.java +++ b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentStatusChangedMapper.java @@ -1,20 +1,18 @@ package dev.vality.newway.mapper.payment; -import dev.vality.damsel.domain.InvoicePaymentCaptured; import dev.vality.damsel.domain.InvoicePaymentStatus; import dev.vality.damsel.payment_processing.InvoiceChange; -import dev.vality.geck.common.util.TBaseUtil; +import dev.vality.geck.common.util.TypeUtil; import dev.vality.geck.filter.Filter; import dev.vality.geck.filter.PathConditionFilter; import dev.vality.geck.filter.condition.IsNullCondition; import dev.vality.geck.filter.rule.PathConditionRule; import dev.vality.machinegun.eventsink.MachineEvent; -import dev.vality.newway.domain.enums.PaymentStatus; -import dev.vality.newway.domain.tables.pojos.Payment; -import dev.vality.newway.handler.event.stock.LocalStorage; +import dev.vality.newway.domain.tables.pojos.PaymentStatusInfo; +import dev.vality.newway.mapper.Mapper; +import dev.vality.newway.model.InvoicingKey; import dev.vality.newway.model.PaymentWrapper; -import dev.vality.newway.service.PaymentWrapperService; -import dev.vality.newway.util.JsonUtil; +import dev.vality.newway.factory.invoice.payment.PaymentStatusInfoFactory; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -22,53 +20,37 @@ @Slf4j @Component @RequiredArgsConstructor -public class InvoicePaymentStatusChangedMapper extends AbstractInvoicingPaymentMapper { +public class InvoicePaymentStatusChangedMapper implements Mapper { - private final PaymentWrapperService paymentWrapperService; - - private Filter filter = new PathConditionFilter( - new PathConditionRule("invoice_payment_change.payload.invoice_payment_status_changed", - new IsNullCondition().not())); + private Filter filter = new PathConditionFilter(new PathConditionRule( + "invoice_payment_change.payload.invoice_payment_status_changed", + new IsNullCondition().not() + )); @Override - public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId, LocalStorage storage) { + public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId) { InvoicePaymentStatus invoicePaymentStatus = change.getInvoicePaymentChange().getPayload().getInvoicePaymentStatusChanged().getStatus(); long sequenceId = event.getEventId(); String invoiceId = event.getSourceId(); String paymentId = change.getInvoicePaymentChange().getId(); - log.info("Start payment status changed mapping, sequenceId={}, invoiceId={}, paymentId={}, status={}", - sequenceId, invoiceId, paymentId, invoicePaymentStatus.getSetField().getFieldName()); - - PaymentWrapper paymentWrapper = paymentWrapperService.get(invoiceId, paymentId, sequenceId, changeId, storage); - if (paymentWrapper == null) { - return null; - } - paymentWrapper.setShouldInsert(true); - Payment paymentSource = paymentWrapper.getPayment(); - setInsertProperties(paymentSource, sequenceId, changeId, event.getCreatedAt()); - paymentSource.setStatus(TBaseUtil.unionFieldToEnum(invoicePaymentStatus, PaymentStatus.class)); - if (invoicePaymentStatus.isSetCancelled()) { - paymentSource.setStatusCancelledReason(invoicePaymentStatus.getCancelled().getReason()); - paymentSource.setStatusCapturedReason(null); - paymentSource.setStatusFailedFailure(null); - } else if (invoicePaymentStatus.isSetCaptured()) { - paymentSource.setStatusCancelledReason(null); - InvoicePaymentCaptured invoicePaymentCaptured = invoicePaymentStatus.getCaptured(); - paymentSource.setStatusCapturedReason(invoicePaymentCaptured.getReason()); - if (invoicePaymentCaptured.isSetCost()) { - paymentSource.setAmount(invoicePaymentCaptured.getCost().getAmount()); - paymentSource.setCurrencyCode(invoicePaymentCaptured.getCost().getCurrency().getSymbolicCode()); - } - paymentSource.setStatusFailedFailure(null); - } else if (invoicePaymentStatus.isSetFailed()) { - paymentSource.setStatusCancelledReason(null); - paymentSource.setStatusCapturedReason(null); - paymentSource.setStatusFailedFailure(JsonUtil.thriftBaseToJsonString(invoicePaymentStatus.getFailed())); - } - log.info("Payment status has been mapped, sequenceId={}, invoiceId={}, paymentId={}, status={}", - sequenceId, invoiceId, paymentId, invoicePaymentStatus.getSetField().getFieldName()); + log.info("Start payment status changed mapping, sequenceId={}, changeId={}, invoiceId={}, paymentId={}, status={}", + sequenceId, changeId, invoiceId, paymentId, invoicePaymentStatus.getSetField().getFieldName()); + + PaymentStatusInfo statusInfo = PaymentStatusInfoFactory.build( + invoicePaymentStatus, + invoiceId, + paymentId, + TypeUtil.stringToLocalDateTime(event.getCreatedAt()), + changeId, + sequenceId + ); + log.info("Payment status has been mapped, sequenceId={}, changeId={}, invoiceId={}, paymentId={}, status={}", + sequenceId, changeId, invoiceId, paymentId, invoicePaymentStatus.getSetField().getFieldName()); + PaymentWrapper paymentWrapper = new PaymentWrapper(); + paymentWrapper.setKey(InvoicingKey.buildKey(invoiceId, paymentId)); + paymentWrapper.setPaymentStatusInfo(statusInfo); return paymentWrapper; } diff --git a/src/main/java/dev/vality/newway/util/CashFlowType.java b/src/main/java/dev/vality/newway/model/CashFlowType.java similarity index 95% rename from src/main/java/dev/vality/newway/util/CashFlowType.java rename to src/main/java/dev/vality/newway/model/CashFlowType.java index 9a8f8a53..816e5ebe 100644 --- a/src/main/java/dev/vality/newway/util/CashFlowType.java +++ b/src/main/java/dev/vality/newway/model/CashFlowType.java @@ -1,4 +1,4 @@ -package dev.vality.newway.util; +package dev.vality.newway.model; import dev.vality.damsel.domain.*; @@ -41,9 +41,9 @@ public enum CashFlowType { CashFlowAccount.merchant(MerchantCashFlowAccount.guarantee) ); - private List sources; + private final List sources; - private List destinations; + private final List destinations; CashFlowType(CashFlowAccount source, CashFlowAccount destination) { this(Collections.singletonList(source), Collections.singletonList(destination)); diff --git a/src/main/java/dev/vality/newway/model/CashFlowWrapper.java b/src/main/java/dev/vality/newway/model/CashFlowWrapper.java new file mode 100644 index 00000000..f7a0d85c --- /dev/null +++ b/src/main/java/dev/vality/newway/model/CashFlowWrapper.java @@ -0,0 +1,17 @@ +package dev.vality.newway.model; + +import dev.vality.newway.domain.tables.pojos.CashFlow; +import dev.vality.newway.domain.tables.pojos.CashFlowLink; +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@Data +@RequiredArgsConstructor +public class CashFlowWrapper { + + private final CashFlowLink cashFlowLink; + private final List cashFlows; + +} diff --git a/src/main/java/dev/vality/newway/model/InvoicePaymentEventIdHolder.java b/src/main/java/dev/vality/newway/model/InvoicePaymentEventIdHolder.java new file mode 100644 index 00000000..2c408aea --- /dev/null +++ b/src/main/java/dev/vality/newway/model/InvoicePaymentEventIdHolder.java @@ -0,0 +1,11 @@ +package dev.vality.newway.model; + +import lombok.Data; + +@Data +public class InvoicePaymentEventIdHolder { + private final String invoiceId; + private final String paymentId; + private final Long sequenceId; + private final Integer changeId; +} diff --git a/src/main/java/dev/vality/newway/model/InvoiceWrapper.java b/src/main/java/dev/vality/newway/model/InvoiceWrapper.java index 78b152ac..ebb90037 100644 --- a/src/main/java/dev/vality/newway/model/InvoiceWrapper.java +++ b/src/main/java/dev/vality/newway/model/InvoiceWrapper.java @@ -2,12 +2,11 @@ import dev.vality.newway.domain.tables.pojos.Invoice; import dev.vality.newway.domain.tables.pojos.InvoiceCart; +import dev.vality.newway.domain.tables.pojos.InvoiceStatusInfo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import org.springframework.beans.BeanUtils; -import java.util.ArrayList; import java.util.List; @Data @@ -15,22 +14,6 @@ @AllArgsConstructor public class InvoiceWrapper { private Invoice invoice; + private InvoiceStatusInfo invoiceStatusInfo; private List carts; - - public InvoiceWrapper copy() { - Invoice invoiceTarget = new Invoice(); - BeanUtils.copyProperties(invoice, invoiceTarget); - InvoiceWrapper invoiceWrapperTarget = new InvoiceWrapper(); - invoiceWrapperTarget.setInvoice(invoiceTarget); - if (carts != null) { - List cartsTarget = new ArrayList<>(); - carts.forEach(c -> { - InvoiceCart cartTarget = new InvoiceCart(); - BeanUtils.copyProperties(c, cartTarget); - cartsTarget.add(cartTarget); - }); - invoiceWrapperTarget.setCarts(cartsTarget); - } - return invoiceWrapperTarget; - } } diff --git a/src/main/java/dev/vality/newway/model/PartyShop.java b/src/main/java/dev/vality/newway/model/PartyShop.java new file mode 100644 index 00000000..471d0739 --- /dev/null +++ b/src/main/java/dev/vality/newway/model/PartyShop.java @@ -0,0 +1,12 @@ +package dev.vality.newway.model; + +import lombok.Data; + +@Data +public class PartyShop { + + private final String partyId; + + private final String shopId; + +} diff --git a/src/main/java/dev/vality/newway/model/PaymentWrapper.java b/src/main/java/dev/vality/newway/model/PaymentWrapper.java index 8d7baeb9..e7336bdd 100644 --- a/src/main/java/dev/vality/newway/model/PaymentWrapper.java +++ b/src/main/java/dev/vality/newway/model/PaymentWrapper.java @@ -1,39 +1,22 @@ package dev.vality.newway.model; -import dev.vality.newway.domain.tables.pojos.CashFlow; -import dev.vality.newway.domain.tables.pojos.Payment; +import dev.vality.newway.domain.tables.pojos.*; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import org.springframework.beans.BeanUtils; - -import java.util.ArrayList; -import java.util.List; @Data @NoArgsConstructor @AllArgsConstructor public class PaymentWrapper { private Payment payment; - private List cashFlows; - private boolean shouldInsert; + private PaymentStatusInfo paymentStatusInfo; + private PaymentPayerInfo paymentPayerInfo; + private PaymentAdditionalInfo paymentAdditionalInfo; + private PaymentRecurrentInfo paymentRecurrentInfo; + private PaymentRiskData paymentRiskData; + private PaymentFee paymentFee; + private PaymentRoute paymentRoute; + private CashFlowWrapper cashFlowWrapper; private InvoicingKey key; - - public PaymentWrapper copy() { - Payment paymentTarget = new Payment(); - BeanUtils.copyProperties(payment, paymentTarget); - PaymentWrapper paymentWrapperTarget = new PaymentWrapper(); - paymentWrapperTarget.setKey(InvoicingKey.buildKey(this)); - paymentWrapperTarget.setPayment(paymentTarget); - if (cashFlows != null) { - List cashFlowsTarget = new ArrayList<>(); - cashFlows.forEach(c -> { - CashFlow cartTarget = new CashFlow(); - BeanUtils.copyProperties(c, cartTarget); - cashFlowsTarget.add(cartTarget); - }); - paymentWrapperTarget.setCashFlows(cashFlowsTarget); - } - return paymentWrapperTarget; - } } diff --git a/src/main/java/dev/vality/newway/service/CashFlowService.java b/src/main/java/dev/vality/newway/service/CashFlowService.java index a767797d..18a6c771 100644 --- a/src/main/java/dev/vality/newway/service/CashFlowService.java +++ b/src/main/java/dev/vality/newway/service/CashFlowService.java @@ -3,17 +3,16 @@ import dev.vality.newway.dao.invoicing.iface.CashFlowDao; import dev.vality.newway.domain.enums.PaymentChangeType; import dev.vality.newway.domain.tables.pojos.CashFlow; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import java.util.List; +@RequiredArgsConstructor @Component public class CashFlowService { - private final CashFlowDao cashFlowDao; - public CashFlowService(CashFlowDao cashFlowDao) { - this.cashFlowDao = cashFlowDao; - } + private final CashFlowDao cashFlowDao; public void save(Long objSourceId, Long objId, PaymentChangeType type) { if (objId != null) { diff --git a/src/main/java/dev/vality/newway/service/InvoiceBatchService.java b/src/main/java/dev/vality/newway/service/InvoiceBatchService.java deleted file mode 100644 index 06222f47..00000000 --- a/src/main/java/dev/vality/newway/service/InvoiceBatchService.java +++ /dev/null @@ -1,52 +0,0 @@ -package dev.vality.newway.service; - -import dev.vality.newway.dao.invoicing.iface.InvoiceDao; -import dev.vality.newway.dao.invoicing.impl.InvoiceIdsGeneratorDaoImpl; -import dev.vality.newway.model.InvoiceWrapper; -import dev.vality.newway.model.InvoicingKey; -import dev.vality.newway.model.InvoicingType; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -@Service -@Slf4j -@RequiredArgsConstructor -public class InvoiceBatchService { - - private final InvoiceDao invoiceDao; - private final InvoiceWrapperService invoiceWrapperService; - private final InvoiceIdsGeneratorDaoImpl invoiceIdsGeneratorDao; - - public void process(List invoiceWrappers) { - log.info("Start processing of invoice batch, size={}", invoiceWrappers.size()); - List ids = invoiceIdsGeneratorDao.get(invoiceWrappers.size()); - setIds(invoiceWrappers, ids); - invoiceWrapperService.save(invoiceWrappers); - Collection invoicingSwitchIds = invoiceWrappers.stream().collect( - Collectors - .groupingBy(i -> new InvoicingKey(i.getInvoice().getInvoiceId(), null, InvoicingType.INVOICE))) - .keySet(); - log.info("Switch to current ids: {}", invoicingSwitchIds); - invoiceDao.switchCurrent(invoicingSwitchIds); - log.info("End processing of invoice batch"); - } - - private void setIds(List invoiceWrappers, List ids) { - for (int i = 0; i < invoiceWrappers.size(); ++i) { - InvoiceWrapper invoiceWrapper = invoiceWrappers.get(i); - Long invId = ids.get(i); - invoiceWrapper.getInvoice().setId(invId); - if (invoiceWrapper.getCarts() != null) { - invoiceWrapper.getCarts().forEach(c -> { - c.setId(null); - c.setInvId(invId); - }); - } - } - } -} diff --git a/src/main/java/dev/vality/newway/service/InvoiceWrapperService.java b/src/main/java/dev/vality/newway/service/InvoiceWrapperService.java index bfc34dcb..0a560669 100644 --- a/src/main/java/dev/vality/newway/service/InvoiceWrapperService.java +++ b/src/main/java/dev/vality/newway/service/InvoiceWrapperService.java @@ -1,55 +1,26 @@ package dev.vality.newway.service; -import com.github.benmanes.caffeine.cache.Cache; -import dev.vality.newway.dao.invoicing.iface.InvoiceCartDao; -import dev.vality.newway.dao.invoicing.iface.InvoiceDao; -import dev.vality.newway.domain.tables.pojos.Invoice; -import dev.vality.newway.domain.tables.pojos.InvoiceCart; -import dev.vality.newway.exception.DaoException; -import dev.vality.newway.exception.NotFoundException; -import dev.vality.newway.handler.event.stock.LocalStorage; +import dev.vality.newway.handler.wrapper.WrapperHandler; import dev.vality.newway.model.InvoiceWrapper; -import dev.vality.newway.model.InvoicingKey; -import dev.vality.newway.model.InvoicingType; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; +import java.util.*; +@Slf4j @Service @RequiredArgsConstructor public class InvoiceWrapperService { - private final InvoiceDao invoiceDao; - private final InvoiceCartDao invoiceCartDao; - private final Cache invoiceDataCache; - - public InvoiceWrapper get(String invoiceId, LocalStorage storage) throws DaoException, NotFoundException { - InvoicingKey key = InvoicingKey.builder().invoiceId(invoiceId).type(InvoicingType.INVOICE).build(); - InvoiceWrapper invoiceWrapper = (InvoiceWrapper) storage.get(key); - if (invoiceWrapper != null) { - return invoiceWrapper.copy(); - } - invoiceWrapper = invoiceDataCache.getIfPresent(key); - if (invoiceWrapper != null) { - return invoiceWrapper.copy(); - } - Invoice invoice = invoiceDao.get(invoiceId); - List carts = invoiceCartDao.getByInvId(invoice.getId()); - return new InvoiceWrapper(invoice, carts); - } + private final List> invoiceWrapperHandlers; public void save(List invoiceWrappers) { - invoiceWrappers.forEach(i -> invoiceDataCache - .put(InvoicingKey.builder().invoiceId(i.getInvoice().getInvoiceId()).type(InvoicingType.INVOICE) - .build(), i)); - List invoices = invoiceWrappers.stream().map(InvoiceWrapper::getInvoice).collect(Collectors.toList()); - invoiceDao.saveBatch(invoices); - List carts = - invoiceWrappers.stream().filter(i -> i.getCarts() != null).map(InvoiceWrapper::getCarts) - .flatMap(Collection::stream).collect(Collectors.toList()); - invoiceCartDao.save(carts); + log.info("Start saving of invoice batch, size={}", invoiceWrappers.size()); + invoiceWrapperHandlers.stream() + .filter(handler -> handler.accept(invoiceWrappers)) + .forEach(handler -> handler.saveBatch(invoiceWrappers)); + log.info("Saved invoice batch"); } + } diff --git a/src/main/java/dev/vality/newway/service/InvoicingService.java b/src/main/java/dev/vality/newway/service/InvoicingService.java index 10bddda1..d1420fe6 100644 --- a/src/main/java/dev/vality/newway/service/InvoicingService.java +++ b/src/main/java/dev/vality/newway/service/InvoicingService.java @@ -3,11 +3,9 @@ import dev.vality.damsel.payment_processing.EventPayload; import dev.vality.damsel.payment_processing.InvoiceChange; import dev.vality.machinegun.eventsink.MachineEvent; -import dev.vality.newway.handler.event.stock.LocalStorage; import dev.vality.newway.handler.event.stock.impl.invoicing.InvoicingHandler; -import dev.vality.newway.mapper.AbstractInvoicingMapper; +import dev.vality.newway.mapper.Mapper; import dev.vality.newway.model.InvoiceWrapper; -import dev.vality.newway.model.InvoicingKey; import dev.vality.newway.model.PaymentWrapper; import dev.vality.sink.common.parser.impl.MachineEventParser; import lombok.RequiredArgsConstructor; @@ -18,6 +16,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; @Slf4j @Service @@ -25,71 +24,79 @@ public class InvoicingService { private final List otherHandlers; - private final List> invoiceMappers; - private final List> paymentMappers; - private final InvoiceBatchService invoiceBatchService; - private final PaymentBatchService paymentBatchService; + private final List> invoiceMappers; + private final List> paymentMappers; + private final PartyShopCacheService partyShopCacheService; + private final InvoiceWrapperService invoiceWrapperService; + private final PaymentWrapperService paymentWrapperService; private final MachineEventParser parser; @Transactional(propagation = Propagation.REQUIRED) public void handleEvents(List machineEvents) { - LocalStorage storage = new LocalStorage(); List invoices = new ArrayList<>(machineEvents.size()); List payments = new ArrayList<>(machineEvents.size()); - machineEvents.forEach(me -> { - EventPayload payload = parser.parse(me); - if (payload.isSetInvoiceChanges()) { - try { - List invoiceChanges = payload.getInvoiceChanges(); - for (int i = 0; i < invoiceChanges.size(); i++) { - InvoiceChange change = invoiceChanges.get(i); - InvoiceWrapper invoiceWrapper = mapInvoice(change, me, i, storage); - PaymentWrapper paymentWrapper = mapPayment(change, me, i, storage); - if (invoiceWrapper != null) { - invoices.add(invoiceWrapper); - storage.put(InvoicingKey.buildKey(invoiceWrapper), invoiceWrapper); - } - if (paymentWrapper != null) { - payments.add(paymentWrapper); - storage.put(InvoicingKey.buildKey(paymentWrapper), paymentWrapper); - } - handleOtherEvent(change, me, i); - } - } catch (Throwable e) { - log.error("Unexpected error while handling events; machineId: {}, eventId: {}", me.getSourceId(), - me.getEventId(), e); - throw e; - } - } - }); + machineEvents.forEach(me -> processMachineEvents(invoices, payments, me)); if (!invoices.isEmpty()) { - invoiceBatchService.process(invoices); + invoiceWrapperService.save(invoices); } if (!payments.isEmpty()) { - paymentBatchService.process(payments); + paymentWrapperService.save(payments); } } - private void handleOtherEvent(InvoiceChange change, MachineEvent me, int i) { + private void processMachineEvents(List invoices, + List payments, + MachineEvent machineEvent) { + EventPayload payload = parser.parse(machineEvent); + if (payload.isSetInvoiceChanges()) { + try { + List invoiceChanges = payload.getInvoiceChanges(); + for (int changeId = 0; changeId < invoiceChanges.size(); changeId++) { + InvoiceChange change = invoiceChanges.get(changeId); + handleInvoiceEvent(invoices, change, machineEvent, changeId); + handlePaymentEvent(payments, change, machineEvent, changeId); + handleOtherEvent(change, machineEvent, changeId); + } + } catch (Throwable e) { + log.error("Unexpected error while handling events; machineId: {}, eventId: {}", + machineEvent.getSourceId(), machineEvent.getEventId(), e); + throw e; + } + } + } + + private void handleOtherEvent(InvoiceChange change, MachineEvent me, int changeId) { otherHandlers.stream() .filter(m -> m.accept(change)) .findFirst() - .ifPresent(m -> m.handle(change, me, i)); + .ifPresent(m -> m.handle(change, me, changeId)); } - private PaymentWrapper mapPayment(InvoiceChange change, MachineEvent me, int i, LocalStorage storage) { - return paymentMappers.stream() - .filter(m -> m.accept(change)) - .findFirst() - .map(m -> m.map(change, me, i, storage)) - .orElse(null); + private void handlePaymentEvent(List payments, + InvoiceChange change, + MachineEvent machineEvent, + int changeId) { + mapEntity(paymentMappers, change, machineEvent, changeId).ifPresent(payments::add); + } + + private void handleInvoiceEvent(List invoices, + InvoiceChange change, + MachineEvent machineEvent, + int changeId) { + mapEntity(invoiceMappers, change, machineEvent, changeId) + .ifPresent(wrapper -> { + invoices.add(wrapper); + partyShopCacheService.put(wrapper); + }); } - private InvoiceWrapper mapInvoice(InvoiceChange change, MachineEvent me, int i, LocalStorage storage) { - return invoiceMappers.stream() + private Optional mapEntity(List> mappers, + InvoiceChange change, + MachineEvent machineEvent, + int changeId) { + return mappers.stream() .filter(m -> m.accept(change)) .findFirst() - .map(m -> m.map(change, me, i, storage)) - .orElse(null); + .map(m -> m.map(change, machineEvent, changeId)); } } diff --git a/src/main/java/dev/vality/newway/service/PartyShopCacheService.java b/src/main/java/dev/vality/newway/service/PartyShopCacheService.java new file mode 100644 index 00000000..15336dfb --- /dev/null +++ b/src/main/java/dev/vality/newway/service/PartyShopCacheService.java @@ -0,0 +1,49 @@ +package dev.vality.newway.service; + +import com.github.benmanes.caffeine.cache.Cache; +import dev.vality.newway.dao.invoicing.iface.InvoiceDao; +import dev.vality.newway.domain.tables.pojos.Invoice; +import dev.vality.newway.model.InvoiceWrapper; +import dev.vality.newway.model.PartyShop; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@RequiredArgsConstructor +@Service +public class PartyShopCacheService { + + private final InvoiceDao invoiceDao; + private final Cache partyShopDataCache; + + public void put(InvoiceWrapper invoiceWrapper) { + if (invoiceWrapper.getInvoice() == null) { + return; + } + partyShopDataCache.put( + invoiceWrapper.getInvoice().getInvoiceId(), + new PartyShop( + invoiceWrapper.getInvoice().getPartyId(), + invoiceWrapper.getInvoice().getShopId() + ) + ); + } + + public PartyShop get(String invoiceId) { + PartyShop partyShop = partyShopDataCache.getIfPresent(invoiceId); + if (partyShop != null) { + return new PartyShop(partyShop.getPartyId(), partyShop.getShopId()); + } + log.info("Cache miss for invoiceId: {}", invoiceId); + Invoice invoice = invoiceDao.get(invoiceId); + if (invoice == null) { + log.warn("Invoice was not found for invoiceId: {}", invoiceId); + return null; + } + partyShop = new PartyShop(invoice.getPartyId(), invoice.getShopId()); + partyShopDataCache.put(invoiceId, partyShop); + return partyShop; + } + +} diff --git a/src/main/java/dev/vality/newway/service/PaymentBatchService.java b/src/main/java/dev/vality/newway/service/PaymentBatchService.java deleted file mode 100644 index b3160315..00000000 --- a/src/main/java/dev/vality/newway/service/PaymentBatchService.java +++ /dev/null @@ -1,40 +0,0 @@ -package dev.vality.newway.service; - -import dev.vality.newway.dao.invoicing.impl.PaymentIdsGeneratorDaoImpl; -import dev.vality.newway.model.InvoicingKey; -import dev.vality.newway.model.PaymentWrapper; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; - -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -@Service -@Slf4j -@RequiredArgsConstructor -public class PaymentBatchService { - - private final PaymentWrapperService paymentWrapperService; - private final PaymentIdsGeneratorDaoImpl paymentIdsGeneratorDao; - private final PaymentSquashService paymentSquashService; - - public void process(List paymentWrappers) { - log.info("Start processing of payment batch, size={}", paymentWrappers.size()); - List ids = paymentIdsGeneratorDao.get(paymentWrappers.size()); - List squashedPaymentWrappers = paymentSquashService.squash(paymentWrappers, ids); - log.info("After squash size={}", squashedPaymentWrappers.size()); - paymentWrapperService.save(squashedPaymentWrappers); - Collection invoicingSwitchIds = squashedPaymentWrappers - .stream() - .filter(PaymentWrapper::isShouldInsert) - .collect(Collectors.groupingBy(PaymentWrapper::getKey)).keySet(); - if (!CollectionUtils.isEmpty(invoicingSwitchIds)) { - log.info("Switch to current ids: {}", invoicingSwitchIds); - paymentWrapperService.switchCurrent(invoicingSwitchIds); - } - log.info("End processing of payment batch"); - } -} diff --git a/src/main/java/dev/vality/newway/service/PaymentSquashService.java b/src/main/java/dev/vality/newway/service/PaymentSquashService.java deleted file mode 100644 index b55dce3e..00000000 --- a/src/main/java/dev/vality/newway/service/PaymentSquashService.java +++ /dev/null @@ -1,74 +0,0 @@ -package dev.vality.newway.service; - -import dev.vality.newway.model.PaymentWrapper; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.stream.Collectors; - -@Slf4j -@Service -public class PaymentSquashService { - - public List squash(List wrappers, List ids) { - var groupedMap = wrappers.stream() - .collect(Collectors.groupingBy(PaymentWrapper::getKey, LinkedHashMap::new, Collectors.toList())); - List result = new ArrayList<>(); - Iterator iterator = ids.iterator(); - groupedMap.forEach((key, pwList) -> { - setIds(pwList, iterator); - List squashedById = squashById(pwList); - long count = squashedById.stream().filter(w -> !w.isShouldInsert()).count(); - if (count > 1) { - throw new IllegalStateException("Must be less or equal than one update statements, " + pwList); - } - result.addAll(squashedById); - }); - return result; - } - - private List squashById(List pwList) { - List result = new ArrayList<>(); - var groupedById = - pwList.stream().collect(Collectors.groupingBy(this::getId, LinkedHashMap::new, Collectors.toList())); - groupedById.forEach((id, ws) -> { - boolean shouldInsert = ws.get(0).isShouldInsert(); - PaymentWrapper pwLast = ws.get(ws.size() - 1); - pwLast.setShouldInsert(shouldInsert); - result.add(pwLast); - }); - return result; - } - - private void setIds(List pwList, Iterator iterator) { - PaymentWrapper wrapInsert = pwList.get(0); - for (PaymentWrapper w : pwList) { - Long id; - if (w.isShouldInsert()) { - wrapInsert = w; - id = iterator.next(); - } else { - id = getId(wrapInsert); - } - setId(w, id); - } - } - - private void setId(PaymentWrapper paymentWrapper, Long id) { - paymentWrapper.getPayment().setId(id); - if (paymentWrapper.getCashFlows() != null) { - paymentWrapper.getCashFlows().forEach(c -> { - c.setId(null); - c.setObjId(id); - }); - } - } - - private Long getId(PaymentWrapper paymentWrapper) { - return paymentWrapper.getPayment().getId(); - } -} diff --git a/src/main/java/dev/vality/newway/service/PaymentWrapperService.java b/src/main/java/dev/vality/newway/service/PaymentWrapperService.java index b00f5954..ab46d5b0 100644 --- a/src/main/java/dev/vality/newway/service/PaymentWrapperService.java +++ b/src/main/java/dev/vality/newway/service/PaymentWrapperService.java @@ -1,93 +1,26 @@ package dev.vality.newway.service; -import com.github.benmanes.caffeine.cache.Cache; -import dev.vality.newway.dao.invoicing.iface.CashFlowDao; -import dev.vality.newway.dao.invoicing.iface.PaymentDao; -import dev.vality.newway.domain.enums.PaymentChangeType; -import dev.vality.newway.domain.tables.pojos.CashFlow; -import dev.vality.newway.domain.tables.pojos.Payment; -import dev.vality.newway.exception.DaoException; -import dev.vality.newway.exception.NotFoundException; -import dev.vality.newway.handler.event.stock.LocalStorage; -import dev.vality.newway.model.InvoicingKey; +import dev.vality.newway.handler.wrapper.WrapperHandler; import dev.vality.newway.model.PaymentWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; +import java.util.*; @Slf4j @Service @RequiredArgsConstructor public class PaymentWrapperService { - private final PaymentDao paymentDao; - private final CashFlowDao cashFlowDao; - private final Cache paymentDataCache; - - public PaymentWrapper get(String invoiceId, String paymentId, - long sequenceId, Integer changeId, - LocalStorage storage) throws DaoException, NotFoundException { - InvoicingKey key = InvoicingKey.buildKey(invoiceId, paymentId); - PaymentWrapper paymentWrapper = (PaymentWrapper) storage.get(key); - if (paymentWrapper != null) { - paymentWrapper = paymentWrapper.copy(); - } else { - paymentWrapper = paymentDataCache.getIfPresent(key); - if (paymentWrapper != null) { - paymentWrapper = paymentWrapper.copy(); - } else { - Payment payment = paymentDao.get(invoiceId, paymentId); - List cashFlows = cashFlowDao.getByObjId(payment.getId(), PaymentChangeType.payment); - paymentWrapper = new PaymentWrapper(); - paymentWrapper.setPayment(payment); - paymentWrapper.setCashFlows(cashFlows); - paymentWrapper.setKey(key); - } - } - if ((paymentWrapper.getPayment().getSequenceId() > sequenceId) - || (paymentWrapper.getPayment().getSequenceId() == sequenceId - && paymentWrapper.getPayment().getChangeId() >= changeId)) { - paymentWrapper = null; - } - return paymentWrapper; - } + private final List> paymentWrapperHandlers; public void save(List paymentWrappers) { - paymentWrappers.forEach(pw -> paymentDataCache.put(pw.getKey(), pw)); - List paymentsForInsert = paymentWrappers.stream() - .filter(PaymentWrapper::isShouldInsert) - .map(PaymentWrapper::getPayment) - .collect(Collectors.toList()); - List paymentsForUpdate = paymentWrappers.stream() - .filter(pw -> !pw.isShouldInsert()) - .map(PaymentWrapper::getPayment) - .collect(Collectors.toList()); - List cashFlows = paymentWrappers - .stream() - .filter(PaymentWrapper::isShouldInsert) - .filter(p -> p.getCashFlows() != null) - .map(PaymentWrapper::getCashFlows) - .flatMap(Collection::stream) - .collect(Collectors.toList()); - if (!CollectionUtils.isEmpty(paymentsForUpdate)) { - log.info("Payments for update: {}", paymentsForUpdate.size()); - paymentDao.updateBatch(paymentsForUpdate); - } - if (!CollectionUtils.isEmpty(paymentsForInsert)) { - log.info("Payments for insert: {}", paymentsForInsert.size()); - paymentDao.saveBatch(paymentsForInsert); - } - if (!CollectionUtils.isEmpty(cashFlows)) { - cashFlowDao.save(cashFlows); - } + log.info("Start saving of payment batch, size={}", paymentWrappers.size()); + paymentWrapperHandlers.stream() + .filter(handler -> handler.accept(paymentWrappers)) + .forEach(handler -> handler.saveBatch(paymentWrappers)); + log.info("Saved payment batch"); } - public void switchCurrent(Collection switchIds) { - paymentDao.switchCurrent(switchIds); - } -} +} \ No newline at end of file diff --git a/src/main/java/dev/vality/newway/util/CashFlowUtil.java b/src/main/java/dev/vality/newway/util/CashFlowUtil.java index 0864ca8e..213b1883 100644 --- a/src/main/java/dev/vality/newway/util/CashFlowUtil.java +++ b/src/main/java/dev/vality/newway/util/CashFlowUtil.java @@ -1,10 +1,7 @@ package dev.vality.newway.util; import dev.vality.damsel.domain.*; -import dev.vality.geck.common.util.TypeUtil; -import dev.vality.newway.domain.enums.AdjustmentCashFlowType; -import dev.vality.newway.domain.enums.PaymentChangeType; -import dev.vality.newway.domain.tables.pojos.CashFlow; +import dev.vality.newway.model.CashFlowType; import java.util.List; import java.util.Map; @@ -102,64 +99,13 @@ private static boolean isExternalOutcome(dev.vality.damsel.domain.CashFlowAccoun && cashFlowAccount.getExternal() == ExternalCashFlowAccount.outcome; } - private static dev.vality.newway.domain.enums.CashFlowAccount getCashFlowAccountType(FinalCashFlowAccount cfa) { - dev.vality.newway.domain.enums.CashFlowAccount sourceAccountType = - TypeUtil.toEnumField(cfa.getAccountType().getSetField().getFieldName(), dev.vality.newway.domain.enums.CashFlowAccount.class); - if (sourceAccountType == null) { - throw new IllegalArgumentException("Illegal cash flow account type: " + cfa.getAccountType()); - } - return sourceAccountType; - } - - private static String getCashFlowAccountTypeValue(FinalCashFlowAccount cfa) { - if (cfa.getAccountType().isSetMerchant()) { - return cfa.getAccountType().getMerchant().name(); - } else if (cfa.getAccountType().isSetProvider()) { - return cfa.getAccountType().getProvider().name(); - } else if (cfa.getAccountType().isSetSystem()) { - return cfa.getAccountType().getSystem().name(); - } else if (cfa.getAccountType().isSetExternal()) { - return cfa.getAccountType().getExternal().name(); - } else if (cfa.getAccountType().isSetWallet()) { - return cfa.getAccountType().getWallet().name(); - } else { - throw new IllegalArgumentException("Illegal cash flow account type: " + cfa.getAccountType()); - } - } - - public static List convertCashFlows(List cashFlowPostings, Long objId, - PaymentChangeType paymentChangeType) { - return convertCashFlows(cashFlowPostings, objId, paymentChangeType, null); - } - - public static List convertCashFlows(List cashFlowPostings, Long objId, - PaymentChangeType paymentChangeType, - AdjustmentCashFlowType adjustmentCashFlowType) { - return cashFlowPostings.stream().map(cf -> { - CashFlow pcf = new CashFlow(); - pcf.setObjId(objId); - pcf.setObjType(paymentChangeType); - pcf.setAdjFlowType(adjustmentCashFlowType); - pcf.setSourceAccountType(CashFlowUtil.getCashFlowAccountType(cf.getSource())); - pcf.setSourceAccountTypeValue(getCashFlowAccountTypeValue(cf.getSource())); - pcf.setSourceAccountId(cf.getSource().getAccountId()); - pcf.setDestinationAccountType(CashFlowUtil.getCashFlowAccountType(cf.getDestination())); - pcf.setDestinationAccountTypeValue(getCashFlowAccountTypeValue(cf.getDestination())); - pcf.setDestinationAccountId(cf.getDestination().getAccountId()); - pcf.setAmount(cf.getVolume().getAmount()); - pcf.setCurrencyCode(cf.getVolume().getCurrency().getSymbolicCode()); - pcf.setDetails(cf.getDetails()); - return pcf; - }).collect(Collectors.toList()); - } - public static Map parseCashFlow(List finalCashFlow) { return parseCashFlow(finalCashFlow, CashFlowType::getCashFlowType); } private static Map parseCashFlow(List finalCashFlow, Function classifier) { - Map collect = finalCashFlow.stream() + return finalCashFlow.stream() .collect( Collectors.groupingBy( classifier, @@ -167,6 +113,5 @@ private static Map parseCashFlow(List ) ) ); - return collect; } } diff --git a/src/main/java/dev/vality/newway/util/PaymentWrapperUtil.java b/src/main/java/dev/vality/newway/util/PaymentWrapperUtil.java new file mode 100644 index 00000000..0d432cbf --- /dev/null +++ b/src/main/java/dev/vality/newway/util/PaymentWrapperUtil.java @@ -0,0 +1,21 @@ +package dev.vality.newway.util; + +import dev.vality.newway.model.InvoicingKey; +import dev.vality.newway.model.PaymentWrapper; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class PaymentWrapperUtil { + + public static Set getInvoicingKeys(List paymentWrappers) { + return paymentWrappers.stream() + .map(PaymentWrapper::getKey) + .collect(Collectors.toSet()); + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6c6f1f82..8bae7297 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,7 +16,7 @@ spring: data-source-properties: reWriteBatchedInserts: true flyway: - schemas: nw + schemas: dw management: server: @@ -43,7 +43,7 @@ management: kafka: bootstrap-servers: localhost:9092 - client-id: newway + client-id: daway ssl: enabled: false trust-store-location: "test" @@ -54,7 +54,7 @@ kafka: key-store-type: PKCS12 trust-store-type: PKCS12 consumer: - group-id: "NewwayListener" + group-id: "DawayListener" enable-auto-commit: false auto-offset-reset: earliest max-poll-records: 20 @@ -82,7 +82,7 @@ kafka: party-management: id: mg-events-party enabled: false - consumer.group-id: "NewwayListenerPartyManagement" + consumer.group-id: "DawayListenerPartyManagement" rate: id: mg-events-rates enabled: false @@ -120,10 +120,11 @@ dmt: enabled: false cache: - invoice: - size: 10000 - payment: + party-shop: size: 10000 + expire: + after: + sec: 600 testcontainers: postgresql: diff --git a/src/main/resources/banner.png b/src/main/resources/banner.png deleted file mode 100644 index a2012a66e809807bc3a05b1747985c8df74a8f1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 109929 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%0DrR~KEetrLl_fCd+>6FvpBiTCp1#lb+vLxt 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 Z6hAJ086g`dX~DdYFEYx~)siN`{{vJ(CYt~N diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 00000000..bd204f87 --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,15 @@ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣠⣤⣤⣶⣶⣶⣶⣶⣤⣤⣀⡀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣄⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⢠⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀ +⠀⠀⠀⠀⠀⣠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣆ +⠀⠀⠀⠀⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣋⣩⣭⣭⣭⣉⡻⣿⣿⣿⣿⣿⣿ +⠀⣠⣴⣭⣹⣟⣿⣿⣿⣿⣿⣿⣿⣿⣿⢣⣼⣿⣿⠛⠁⠘⠿⠿⢻⣿⣿⣿⣿⣿ +⠀⠛⠛⠁⣿⣿⡯⣫⣤⣴⣶⣶⣤⣭⣛⡸⣿⣿⣇⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿ +⠀⠀⢀⣴⣷⠬⣉⣀⣈⣹⣿⣿⣿⣿⣿⣷⣮⣝⣛⣯⣤⣤⣤⣤⣭⣛⠿⣿⣿⣿ +⠀⠈⠉⣽⣶⣶⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠛⢛⡛⠛⠿⣿⣿⣷⡌⢻⣿ +⣀⢰⣿⣦⡝⠛⢷⣮⡛⠻⣿⣿⣿⠿⢛⣫⣵⣶⣿⣿⣿⣿⣿⣿⣿⠿⠛⣣⣾⣿ +⣿⢸⣿⡿⠀⣿⣶⣝⢿⣿⣶⣶⣶⣿⣿⣿⣿⣿⣿⣿⡿⠟⣩⣵⣶⠇⣿⣿⣿⢹ +⣿⢸⣿⠀⡇⢹⣿⣿⡶⠎⣙⠿⠿⠿⠿⠿⢟⣛⣩⣴⣾⣿⣿⣿⡟⣸⣿⣿⠇⣸ +⣿⡇⠛⢠⣿⡀⣿⣿⠀⠀⠀⠈⠛⠻⠿⠿⣿⣿⠿⠿⠛⠛⠛⠁⠀⣿⡿⠃⠀⣿ +⣿⣿⣧⣿⣿⣷⡘⢿⡇⢸⣦⣤⣀⣀⡀⠀⠀⠀⠀⠀⣀⣀⣤⡄⠼⢋⣴⡇⠸⢋ +⣿⣿⣿⣿⣿⣿⣷⣮⡃⣸⣿⣿⣿⣿⣿⣿⣿⣶⣾⣿⣿⣿⣿⣷⣶⣿⡿⢀⣠⣾ \ No newline at end of file diff --git a/src/main/resources/db/migration/V10__commissions.sql b/src/main/resources/db/migration/V10__commissions.sql deleted file mode 100644 index 3af3a2ed..00000000 --- a/src/main/resources/db/migration/V10__commissions.sql +++ /dev/null @@ -1,12 +0,0 @@ -ALTER TABLE nw.payment ADD COLUMN fee BIGINT; -ALTER TABLE nw.payment ADD COLUMN provider_fee BIGINT; -ALTER TABLE nw.payment ADD COLUMN external_fee BIGINT; -ALTER TABLE nw.payment ADD COLUMN guarantee_deposit BIGINT; - -ALTER TABLE nw.refund ADD COLUMN fee BIGINT; -ALTER TABLE nw.refund ADD COLUMN provider_fee BIGINT; -ALTER TABLE nw.refund ADD COLUMN external_fee BIGINT; - -ALTER TABLE nw.adjustment ADD COLUMN fee BIGINT; -ALTER TABLE nw.adjustment ADD COLUMN provider_fee BIGINT; -ALTER TABLE nw.adjustment ADD COLUMN external_fee BIGINT; \ No newline at end of file diff --git a/src/main/resources/db/migration/V11__add_recurrent_payer_type.sql b/src/main/resources/db/migration/V11__add_recurrent_payer_type.sql deleted file mode 100644 index a35a6d29..00000000 --- a/src/main/resources/db/migration/V11__add_recurrent_payer_type.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TYPE nw.payer_type ADD VALUE 'recurrent'; \ No newline at end of file diff --git a/src/main/resources/db/migration/V12__recurrents.sql b/src/main/resources/db/migration/V12__recurrents.sql deleted file mode 100644 index 70b24426..00000000 --- a/src/main/resources/db/migration/V12__recurrents.sql +++ /dev/null @@ -1,4 +0,0 @@ -ALTER TABLE nw.payment ADD COLUMN make_recurrent BOOL; -ALTER TABLE nw.payment ADD COLUMN payer_recurrent_parent_invoice_id CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN payer_recurrent_parent_payment_id CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN recurrent_intention_token CHARACTER VARYING; \ No newline at end of file diff --git a/src/main/resources/db/migration/V13__party_revision_to_refunds.sql b/src/main/resources/db/migration/V13__party_revision_to_refunds.sql deleted file mode 100644 index bfef182a..00000000 --- a/src/main/resources/db/migration/V13__party_revision_to_refunds.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE nw.refund ADD COLUMN party_revision BIGINT; -ALTER TABLE nw.adjustment ADD COLUMN party_revision BIGINT; \ No newline at end of file diff --git a/src/main/resources/db/migration/V14__session_info_remove.sql b/src/main/resources/db/migration/V14__session_info_remove.sql deleted file mode 100644 index 6ed4018f..00000000 --- a/src/main/resources/db/migration/V14__session_info_remove.sql +++ /dev/null @@ -1,21 +0,0 @@ -ALTER TABLE nw.payment DROP COLUMN session_target; -ALTER TABLE nw.payment DROP COLUMN session_payload; -ALTER TABLE nw.payment DROP COLUMN session_payload_finished_result; -ALTER TABLE nw.payment DROP COLUMN session_payload_finished_result_failed_failure_json; -ALTER TABLE nw.payment DROP COLUMN session_payload_suspended_tag; -ALTER TABLE nw.payment DROP COLUMN session_payload_transaction_bound_trx_timestamp; -ALTER TABLE nw.payment DROP COLUMN session_payload_proxy_state_changed_proxy_state; -ALTER TABLE nw.payment DROP COLUMN session_payload_interaction_requested_interaction_json; - -ALTER TABLE nw.refund DROP COLUMN session_target; -ALTER TABLE nw.refund DROP COLUMN session_payload; -ALTER TABLE nw.refund DROP COLUMN session_payload_finished_result; -ALTER TABLE nw.refund DROP COLUMN session_payload_finished_result_failed_failure_json; -ALTER TABLE nw.refund DROP COLUMN session_payload_suspended_tag; -ALTER TABLE nw.refund DROP COLUMN session_payload_transaction_bound_trx_timestamp; -ALTER TABLE nw.refund DROP COLUMN session_payload_proxy_state_changed_proxy_state; -ALTER TABLE nw.refund DROP COLUMN session_payload_interaction_requested_interaction_json; - -DROP TYPE IF EXISTS nw.session_target_status; -DROP TYPE IF EXISTS nw.session_change_payload; -DROP TYPE IF EXISTS nw.session_change_payload_finished_result; \ No newline at end of file diff --git a/src/main/resources/db/migration/V15__1.0.20_fistful_data.sql b/src/main/resources/db/migration/V15__1.0.20_fistful_data.sql deleted file mode 100644 index cb898872..00000000 --- a/src/main/resources/db/migration/V15__1.0.20_fistful_data.sql +++ /dev/null @@ -1,115 +0,0 @@ -CREATE TABLE nw.identity ( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - event_occured_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - sequence_id INT NOT NULL, - party_id CHARACTER VARYING NOT NULL, - party_contract_id CHARACTER VARYING, - identity_id CHARACTER VARYING NOT NULL, - identity_provider_id CHARACTER VARYING NOT NULL, - identity_class_id CHARACTER VARYING NOT NULL, - identity_effective_chalenge_id CHARACTER VARYING, - identity_level_id CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT identity_pkey PRIMARY KEY (id) -); - -CREATE INDEX identity_event_id_idx on nw.identity(event_id); -CREATE INDEX identity_event_created_at_idx on nw.identity(event_created_at); -CREATE INDEX identity_event_occured_at_idx on nw.identity(event_occured_at); -CREATE INDEX identity_id_idx on nw.identity(identity_id); -CREATE INDEX identity_party_id_idx on nw.identity(party_id); - -CREATE TYPE nw.withdrawal_status AS ENUM ('pending', 'succeeded', 'failed'); -CREATE TYPE nw.withdrawal_transfer_status AS ENUM ('created', 'prepared', 'committed', 'cancelled'); - -CREATE TABLE nw.withdrawal ( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - event_occured_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - sequence_id INT NOT NULL, - source_id CHARACTER VARYING NOT NULL, - destination_id CHARACTER VARYING NOT NULL, - withdrawal_id CHARACTER VARYING NOT NULL, - provider_id CHARACTER VARYING, - amount BIGINT NOT NULL, - currency_code CHARACTER VARYING NOT NULL, - withdrawal_status nw.withdrawal_status NOT NULL, - withdrawal_transfer_status nw.withdrawal_transfer_status, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT withdrawal_pkey PRIMARY KEY (id) -); - -CREATE INDEX withdrawal_event_id_idx on nw.withdrawal(event_id); -CREATE INDEX withdrawal_event_created_at_idx on nw.withdrawal(event_created_at); -CREATE INDEX withdrawal_event_occured_at_idx on nw.withdrawal(event_occured_at); -CREATE INDEX withdrawal_id_idx on nw.withdrawal(withdrawal_id); - - -CREATE TABLE nw.fistful_cash_flow ( - id BIGSERIAL NOT NULL, - obj_id BIGINT NOT NULL, - source_account_type nw.cash_flow_account NOT NULL, - source_account_type_value CHARACTER VARYING NOT NULL, - source_account_id CHARACTER VARYING NOT NULL, - destination_account_type nw.cash_flow_account NOT NULL, - destination_account_type_value CHARACTER VARYING NOT NULL, - destination_account_id CHARACTER VARYING NOT NULL, - amount BIGINT NOT NULL, - currency_code CHARACTER VARYING NOT NULL, - details CHARACTER VARYING, - CONSTRAINT fistful_cash_flow_pkey PRIMARY KEY (id) -); - -CREATE INDEX fistful_cash_flow_obj_id_idx on nw.fistful_cash_flow(obj_id); - -CREATE TYPE nw.challenge_status AS ENUM ('pending', 'cancelled', 'completed', 'failed'); -CREATE TYPE nw.challenge_resolution AS ENUM ('approved', 'denied'); - -CREATE TABLE nw.challenge ( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - event_occured_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - sequence_id INT NOT NULL, - identity_id CHARACTER VARYING NOT NULL, - challenge_id CHARACTER VARYING NOT NULL, - challenge_class_id CHARACTER VARYING NOT NULL, - challenge_status nw.challenge_status NOT NULL, - challenge_resolution nw.challenge_resolution, - challenge_valid_until TIMESTAMP WITHOUT TIME ZONE, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT challenge_pkey PRIMARY KEY (id) -); - -CREATE INDEX challenge_event_id_idx on nw.challenge(event_id); -CREATE INDEX challenge_event_created_at_idx on nw.challenge(event_created_at); -CREATE INDEX challenge_event_occured_at_idx on nw.challenge(event_occured_at); -CREATE INDEX challenge_id_idx on nw.challenge(challenge_id); - -CREATE TABLE nw.wallet ( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - event_occured_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - sequence_id INT NOT NULL, - wallet_id CHARACTER VARYING NOT NULL, - wallet_name CHARACTER VARYING NOT NULL, - identity_id CHARACTER VARYING, - party_id CHARACTER VARYING, - currency_code CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT wallet_pkey PRIMARY KEY (id) -); - -CREATE INDEX wallet_event_id_idx on nw.wallet(event_id); -CREATE INDEX wallet_event_created_at_idx on nw.wallet(event_created_at); -CREATE INDEX wallet_event_occured_at_idx on nw.wallet(event_occured_at); -CREATE INDEX wallet_id_idx on nw.wallet(wallet_id); - diff --git a/src/main/resources/db/migration/V16__1.0.23_add_deposit_destination_and_source_event_sink_data.sql b/src/main/resources/db/migration/V16__1.0.23_add_deposit_destination_and_source_event_sink_data.sql deleted file mode 100644 index 0e7a655b..00000000 --- a/src/main/resources/db/migration/V16__1.0.23_add_deposit_destination_and_source_event_sink_data.sql +++ /dev/null @@ -1,123 +0,0 @@ --- clear previous data -delete -from nw.wallet; -delete -from nw.withdrawal; -delete -from nw.identity; -delete -from nw.fistful_cash_flow; - -alter table nw.wallet - add column account_id character varying; -alter table nw.wallet - add column accounter_account_id bigint; -alter table nw.withdrawal - add column fee bigint; -alter table nw.withdrawal - add column provider_fee bigint; -alter table nw.withdrawal - rename column source_id to wallet_id; - -CREATE TYPE nw.fistful_cash_flow_change_type AS ENUM ('withdrawal', 'deposit'); -alter table nw.fistful_cash_flow - add column obj_type nw.fistful_cash_flow_change_type not null; - - -CREATE TYPE nw.source_status AS ENUM ('authorized', 'unauthorized'); - -CREATE TABLE nw.source ( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - event_occured_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - sequence_id INT NOT NULL, - source_id CHARACTER VARYING NOT NULL, - source_name CHARACTER VARYING NOT NULL, - source_status nw.source_status NOT NULL, - resource_internal_details CHARACTER VARYING, - account_id CHARACTER VARYING, - identity_id CHARACTER VARYING, - party_id CHARACTER VARYING, - accounter_account_id BIGINT, - currency_code CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT source_pkey PRIMARY KEY (id) -); - -CREATE INDEX source_event_id_idx - on nw.source (event_id); -CREATE INDEX source_event_created_at_idx - on nw.source (event_created_at); -CREATE INDEX source_event_occured_at_idx - on nw.source (event_occured_at); -CREATE INDEX source_id_idx - on nw.source (source_id); - -CREATE TYPE nw.destination_status AS ENUM ('authorized', 'unauthorized'); - -CREATE TABLE nw.destination ( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - event_occured_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - sequence_id INT NOT NULL, - destination_id CHARACTER VARYING NOT NULL, - destination_name CHARACTER VARYING NOT NULL, - destination_status nw.destination_status NOT NULL, - resource_bank_card_token CHARACTER VARYING NOT NULL, - resource_bank_card_payment_system CHARACTER VARYING, - resource_bank_card_bin CHARACTER VARYING, - resource_bank_card_masked_pan CHARACTER VARYING, - account_id CHARACTER VARYING, - identity_id CHARACTER VARYING, - party_id CHARACTER VARYING, - accounter_account_id BIGINT, - currency_code CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT destination_pkey PRIMARY KEY (id) -); - -CREATE INDEX destination_event_id_idx - on nw.destination (event_id); -CREATE INDEX destination_event_created_at_idx - on nw.destination (event_created_at); -CREATE INDEX destination_event_occured_at_idx - on nw.destination (event_occured_at); -CREATE INDEX destination_id_idx - on nw.destination (destination_id); - -CREATE TYPE nw.deposit_status AS ENUM ('pending', 'succeeded', 'failed'); -CREATE TYPE nw.deposit_transfer_status AS ENUM ('created', 'prepared', 'committed', 'cancelled'); - -CREATE TABLE nw.deposit ( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - event_occured_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - sequence_id INT NOT NULL, - source_id CHARACTER VARYING NOT NULL, - wallet_id CHARACTER VARYING NOT NULL, - deposit_id CHARACTER VARYING NOT NULL, - amount BIGINT NOT NULL, - fee BIGINT, - provider_fee BIGINT, - currency_code CHARACTER VARYING NOT NULL, - deposit_status nw.deposit_status NOT NULL, - deposit_transfer_status nw.deposit_transfer_status, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT deposit_pkey PRIMARY KEY (id) -); - -CREATE INDEX deposit_event_id_idx - on nw.deposit (event_id); -CREATE INDEX deposit_event_created_at_idx - on nw.deposit (event_created_at); -CREATE INDEX deposit_event_occured_at_idx - on nw.deposit (event_occured_at); -CREATE INDEX deposit_id_idx - on nw.deposit (deposit_id); - diff --git a/src/main/resources/db/migration/V17__1.0.25_add_withdrawal_session.sql b/src/main/resources/db/migration/V17__1.0.25_add_withdrawal_session.sql deleted file mode 100644 index 61670a40..00000000 --- a/src/main/resources/db/migration/V17__1.0.25_add_withdrawal_session.sql +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Создание необходимых структур для сессий в БД - */ - -CREATE TYPE nw.BANK_CARD_PAYMENT_SYSTEM AS ENUM ('visa', 'mastercard', 'visaelectron', 'maestro', - 'forbrugsforeningen', 'dankort', 'amex', 'dinersclub', - 'discover', 'unionpay', 'jcb', 'nspkmir'); - -CREATE TYPE nw.WITHDRAWAL_SESSION_STATUS AS ENUM ('active', 'success', 'failed'); - -CREATE TABLE nw.withdrawal_session ( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - event_occured_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - sequence_id INT NOT NULL, - withdrawal_session_id CHARACTER VARYING NOT NULL, - withdrawal_session_status WITHDRAWAL_SESSION_STATUS NOT NULL, - provider_id CHARACTER VARYING NOT NULL, - withdrawal_id CHARACTER VARYING NOT NULL, - destination_name CHARACTER VARYING NOT NULL, - destination_card_token CHARACTER VARYING NOT NULL, - destination_card_payment_system BANK_CARD_PAYMENT_SYSTEM NULL, - destination_card_bin CHARACTER VARYING NULL, - destination_card_masked_pan CHARACTER VARYING NULL, - amount BIGINT NOT NULL, - currency_code CHARACTER VARYING NOT NULL, - sender_party_id CHARACTER VARYING NOT NULL, - sender_provider_id CHARACTER VARYING NOT NULL, - sender_class_id CHARACTER VARYING NOT NULL, - sender_contract_id CHARACTER VARYING NULL, - receiver_party_id CHARACTER VARYING NOT NULL, - receiver_provider_id CHARACTER VARYING NOT NULL, - receiver_class_id CHARACTER VARYING NOT NULL, - receiver_contract_id CHARACTER VARYING NULL, - adapter_state CHARACTER VARYING NULL, - tran_info_id CHARACTER VARYING NULL, - tran_info_timestamp TIMESTAMP WITHOUT TIME ZONE NULL, - tran_info_json CHARACTER VARYING NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT withdrawal_session_PK PRIMARY KEY (id) -); - -CREATE INDEX withdrawal_session_event_id_idx ON nw.withdrawal_session (event_id); -CREATE INDEX withdrawal_session_event_created_at_idx ON nw.withdrawal_session (event_created_at); -CREATE INDEX withdrawal_session_event_occured_at_idx ON nw.withdrawal_session (event_occured_at); -CREATE INDEX withdrawal_session_id_idx ON nw.withdrawal_session (withdrawal_session_id); diff --git a/src/main/resources/db/migration/V18__1.0.26_add_rate.sql b/src/main/resources/db/migration/V18__1.0.26_add_rate.sql deleted file mode 100644 index 60aa1021..00000000 --- a/src/main/resources/db/migration/V18__1.0.26_add_rate.sql +++ /dev/null @@ -1,24 +0,0 @@ -CREATE TABLE nw.rate ( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - source_id CHARACTER VARYING NOT NULL, - lower_bound_inclusive TIMESTAMP WITHOUT TIME ZONE NOT NULL, - upper_bound_exclusive TIMESTAMP WITHOUT TIME ZONE NOT NULL, - source_symbolic_code CHARACTER VARYING NOT NULL, - source_exponent SMALLINT NOT NULL, - destination_symbolic_code CHARACTER VARYING NOT NULL, - destination_exponent SMALLINT NOT NULL, - exchange_rate_rational_p BIGINT NOT NULL, - exchange_rate_rational_q BIGINT NOT NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT rate_pkey PRIMARY KEY (id) -); - -CREATE INDEX rate_event_id_idx - ON nw.rate (event_id); -CREATE INDEX rate_event_created_at_idx - ON nw.rate (event_created_at); -CREATE INDEX rate_source_id_idx - ON nw.rate (source_id); diff --git a/src/main/resources/db/migration/V19__1.0.27_payout_wallet.sql b/src/main/resources/db/migration/V19__1.0.27_payout_wallet.sql deleted file mode 100644 index 8d8e541c..00000000 --- a/src/main/resources/db/migration/V19__1.0.27_payout_wallet.sql +++ /dev/null @@ -1,7 +0,0 @@ -truncate nw.payout cascade; - -alter table nw.payout add column amount bigint; -alter table nw.payout add column fee bigint; -alter table nw.payout add column currency_code character varying; - -alter table nw.payout add column wallet_id character varying; diff --git a/src/main/resources/db/migration/V1__init.sql b/src/main/resources/db/migration/V1__init.sql index d514f79e..0fbee282 100644 --- a/src/main/resources/db/migration/V1__init.sql +++ b/src/main/resources/db/migration/V1__init.sql @@ -1,488 +1,1680 @@ -CREATE SCHEMA IF NOT EXISTS nw; - --- invoices -- - -CREATE TYPE nw.invoice_status AS ENUM('unpaid', 'paid', 'cancelled', 'fulfilled'); - -CREATE TABLE nw.invoice( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - invoice_id CHARACTER VARYING NOT NULL, - party_id CHARACTER VARYING NOT NULL, - shop_id CHARACTER VARYING NOT NULL, - party_revision BIGINT, - created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - status nw.invoice_status NOT NULL, - status_cancelled_details CHARACTER VARYING, - status_fulfilled_details CHARACTER VARYING, - details_product CHARACTER VARYING NOT NULL, - details_description CHARACTER VARYING, - due TIMESTAMP WITHOUT TIME ZONE NOT NULL, - amount BIGINT NOT NULL, - currency_code CHARACTER VARYING NOT NULL, - context BYTEA, - template_id CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT invoice_pkey PRIMARY KEY (id) -); - -CREATE INDEX invoice_event_id on nw.invoice(event_id); -CREATE INDEX invoice_event_created_at on nw.invoice(event_created_at); -CREATE INDEX invoice_invoice_id on nw.invoice(invoice_id); -CREATE INDEX invoice_party_id on nw.invoice(party_id); -CREATE INDEX invoice_status on nw.invoice(status); -CREATE INDEX invoice_created_at on nw.invoice(created_at); - -CREATE TABLE nw.invoice_cart ( - id BIGSERIAL NOT NULL, - inv_id BIGINT NOT NULL, - product CHARACTER VARYING NOT NULL, - quantity INT NOT NULL, - amount BIGINT NOT NULL, - currency_code CHARACTER VARYING NOT NULL, - metadata_json CHARACTER VARYING NOT NULL, - CONSTRAINT invoice_cart_pkey PRIMARY KEY (id), - CONSTRAINT fk_cart_to_invoice FOREIGN KEY (inv_id) REFERENCES nw.invoice(id) -); - -CREATE INDEX invoice_cart_inv_id on nw.invoice_cart(inv_id); - --- payments -- - -CREATE TYPE nw.payment_status AS ENUM ('pending', 'processed', 'captured', 'cancelled', 'refunded', 'failed'); -CREATE TYPE nw.payer_type AS ENUM('payment_resource', 'customer'); -CREATE TYPE nw.payment_tool_type AS ENUM('bank_card', 'payment_terminal', 'digital_wallet'); -CREATE TYPE nw.payment_flow_type AS ENUM('instant', 'hold'); -CREATE TYPE nw.risk_score AS ENUM('low', 'high', 'fatal'); - -CREATE TABLE nw.payment ( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - payment_id CHARACTER VARYING NOT NULL, - created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - invoice_id CHARACTER VARYING NOT NULL, - party_id CHARACTER VARYING NOT NULL, - shop_id CHARACTER VARYING NOT NULL, - domain_revision BIGINT NOT NULL, - party_revision BIGINT, - status nw.payment_status NOT NULL, - status_cancelled_reason CHARACTER VARYING, - status_captured_reason CHARACTER VARYING, - status_failed_failure CHARACTER VARYING, - amount BIGINT NOT NULL, - currency_code CHARACTER VARYING NOT NULL, - payer_type nw.payer_type NOT NULL, - payer_payment_tool_type nw.payment_tool_type NOT NULL, - payer_bank_card_token CHARACTER VARYING, - payer_bank_card_payment_system CHARACTER VARYING, - payer_bank_card_bin CHARACTER VARYING, - payer_bank_card_masked_pan CHARACTER VARYING, - payer_bank_card_token_provider CHARACTER VARYING, - payer_payment_terminal_type CHARACTER VARYING, - payer_digital_wallet_provider CHARACTER VARYING, - payer_digital_wallet_id CHARACTER VARYING, - payer_payment_session_id CHARACTER VARYING, - payer_ip_address CHARACTER VARYING, - payer_fingerprint CHARACTER VARYING, - payer_phone_number CHARACTER VARYING, - payer_email CHARACTER VARYING, - payer_customer_id CHARACTER VARYING, - payer_customer_binding_id CHARACTER VARYING, - payer_customer_rec_payment_tool_id CHARACTER VARYING, - context BYTEA, - payment_flow_type nw.payment_flow_type NOT NULL, - payment_flow_on_hold_expiration CHARACTER VARYING, - payment_flow_held_until TIMESTAMP WITHOUT TIME ZONE, - risk_score nw.risk_score, - route_provider_id INT, - route_terminal_id INT, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT payment_pkey PRIMARY KEY (id) -); - -CREATE INDEX payment_event_id on nw.payment(event_id); -CREATE INDEX payment_event_created_at on nw.payment(event_created_at); -CREATE INDEX payment_invoice_id on nw.payment(invoice_id); -CREATE INDEX payment_party_id on nw.payment(party_id); -CREATE INDEX payment_status on nw.payment(status); -CREATE INDEX payment_created_at on nw.payment(created_at); - -CREATE TYPE nw.cash_flow_account AS ENUM ('merchant', 'provider', 'system', 'external', 'wallet'); - -CREATE TYPE nw.payment_change_type AS ENUM ('payment', 'refund', 'adjustment', 'payout'); - -CREATE TYPE nw.adjustment_cash_flow_type AS ENUM ('new_cash_flow', 'old_cash_flow_inverse'); - -CREATE TABLE nw.cash_flow( - id BIGSERIAL NOT NULL, - obj_id BIGINT NOT NULL, - obj_type nw.payment_change_type NOT NULL, - adj_flow_type nw.adjustment_cash_flow_type, - source_account_type nw.cash_flow_account NOT NULL, - source_account_type_value CHARACTER VARYING NOT NULL, - source_account_id BIGINT NOT NULL, - destination_account_type nw.cash_flow_account NOT NULL, - destination_account_type_value CHARACTER VARYING NOT NULL, - destination_account_id BIGINT NOT NULL, - amount BIGINT NOT NULL, - currency_code CHARACTER VARYING NOT NULL, - details CHARACTER VARYING, - CONSTRAINT cash_flow_pkey PRIMARY KEY (id) -); - -CREATE INDEX cash_flow_idx on nw.cash_flow(obj_id, obj_type); - --- refunds -- - -CREATE TYPE nw.refund_status AS ENUM ('pending', 'succeeded', 'failed'); - -CREATE TABLE nw.refund ( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - domain_revision BIGINT NOT NULL, - refund_id CHARACTER VARYING NOT NULL, - payment_id CHARACTER VARYING NOT NULL, - invoice_id CHARACTER VARYING NOT NULL, - party_id CHARACTER VARYING NOT NULL, - shop_id CHARACTER VARYING NOT NULL, - created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - status nw.refund_status NOT NULL, - status_failed_failure CHARACTER VARYING, - amount BIGINT, - currency_code CHARACTER VARYING, - reason CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT refund_pkey PRIMARY KEY (id) -); - -CREATE INDEX refund_event_id on nw.refund(event_id); -CREATE INDEX refund_event_created_at on nw.refund(event_created_at); -CREATE INDEX refund_invoice_id on nw.refund(invoice_id); -CREATE INDEX refund_party_id on nw.refund(party_id); -CREATE INDEX refund_status on nw.refund(status); -CREATE INDEX refund_created_at on nw.refund(created_at); - --- adjustments -- - -CREATE TYPE nw.adjustment_status AS ENUM ('pending', 'captured', 'cancelled'); - -CREATE TABLE nw.adjustment ( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - domain_revision BIGINT NOT NULL, - adjustment_id CHARACTER VARYING NOT NULL, - payment_id CHARACTER VARYING NOT NULL, - invoice_id CHARACTER VARYING NOT NULL, - party_id CHARACTER VARYING NOT NULL, - shop_id CHARACTER VARYING NOT NULL, - created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - status nw.adjustment_status NOT NULL, - status_captured_at TIMESTAMP WITHOUT TIME ZONE, - status_cancelled_at TIMESTAMP WITHOUT TIME ZONE, - reason CHARACTER VARYING NOT NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT adjustment_pkey PRIMARY KEY (id) -); - -CREATE INDEX adjustment_event_id on nw.adjustment(event_id); -CREATE INDEX adjustment_event_created_at on nw.adjustment(event_created_at); -CREATE INDEX adjustment_invoice_id on nw.adjustment(invoice_id); -CREATE INDEX adjustment_party_id on nw.adjustment(party_id); -CREATE INDEX adjustment_status on nw.adjustment(status); -CREATE INDEX adjustment_created_at on nw.adjustment(created_at); - ------------ --- party_mngmnt -- ------------ - -CREATE TYPE nw.blocking AS ENUM ('unblocked', 'blocked'); -CREATE TYPE nw.suspension AS ENUM ('active', 'suspended'); - -CREATE TABLE nw.party( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - party_id CHARACTER VARYING NOT NULL, - contact_info_email CHARACTER VARYING NOT NULL, - created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - blocking nw.blocking NOT NULL, - blocking_unblocked_reason CHARACTER VARYING, - blocking_unblocked_since TIMESTAMP WITHOUT TIME ZONE, - blocking_blocked_reason CHARACTER VARYING, - blocking_blocked_since TIMESTAMP WITHOUT TIME ZONE, - suspension nw.suspension NOT NULL, - suspension_active_since TIMESTAMP WITHOUT TIME ZONE, - suspension_suspended_since TIMESTAMP WITHOUT TIME ZONE, - revision BIGINT NOT NULL, - revision_changed_at TIMESTAMP WITHOUT TIME ZONE, - party_meta_set_ns CHARACTER VARYING, - party_meta_set_data_json CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT party_pkey PRIMARY KEY (id) -); - -CREATE INDEX party_event_id on nw.party(event_id); -CREATE INDEX party_event_created_at on nw.party(event_created_at); -CREATE INDEX party_party_id on nw.party(party_id); -CREATE INDEX party_current on nw.party(current); -CREATE INDEX party_created_at on nw.party(created_at); -CREATE INDEX party_contact_info_email on nw.party(contact_info_email); - --- contract -- - -CREATE TYPE nw.contract_status AS ENUM ('active', 'terminated', 'expired'); -CREATE TYPE nw.representative_document AS ENUM ('articles_of_association', 'power_of_attorney', 'expired'); - -CREATE TABLE nw.contract( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - contract_id CHARACTER VARYING NOT NULL, - party_id CHARACTER VARYING NOT NULL, - payment_institution_id INT, - created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - valid_since TIMESTAMP WITHOUT TIME ZONE, - valid_until TIMESTAMP WITHOUT TIME ZONE, - status nw.contract_status NOT NULL, - status_terminated_at TIMESTAMP WITHOUT TIME ZONE, - terms_id INT NOT NULL, - legal_agreement_signed_at TIMESTAMP WITHOUT TIME ZONE, - legal_agreement_id CHARACTER VARYING, - legal_agreement_valid_until TIMESTAMP WITHOUT TIME ZONE, - report_act_schedule_id INT, - report_act_signer_position CHARACTER VARYING, - report_act_signer_full_name CHARACTER VARYING, - report_act_signer_document nw.representative_document, - report_act_signer_doc_power_of_attorney_signed_at TIMESTAMP WITHOUT TIME ZONE, - report_act_signer_doc_power_of_attorney_legal_agreement_id CHARACTER VARYING, - report_act_signer_doc_power_of_attorney_valid_until TIMESTAMP WITHOUT TIME ZONE, - contractor_id CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT contract_pkey PRIMARY KEY (id) -); - -CREATE INDEX contract_event_id on nw.contract(event_id); -CREATE INDEX contract_event_created_at on nw.contract(event_created_at); -CREATE INDEX contract_contract_id on nw.contract(contract_id); -CREATE INDEX contract_party_id on nw.contract(party_id); -CREATE INDEX contract_created_at on nw.contract(created_at); - -CREATE TABLE nw.contract_adjustment( - id BIGSERIAL NOT NULL, - cntrct_id BIGINT NOT NULL, - contract_adjustment_id CHARACTER VARYING NOT NULL, - created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - valid_since TIMESTAMP WITHOUT TIME ZONE, - valid_until TIMESTAMP WITHOUT TIME ZONE, - terms_id INT NOT NULL, - CONSTRAINT contract_adjustment_pkey PRIMARY KEY (id), - CONSTRAINT fk_adjustment_to_contract FOREIGN KEY (cntrct_id) REFERENCES nw.contract(id) -); - -CREATE INDEX contract_adjustment_idx on nw.contract_adjustment(cntrct_id); - -CREATE TYPE nw.payout_tool_info AS ENUM ('russian_bank_account', 'international_bank_account'); - -CREATE TABLE nw.payout_tool( - id BIGSERIAL NOT NULL, - cntrct_id BIGINT NOT NULL, - payout_tool_id CHARACTER VARYING NOT NULL, - created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - currency_code CHARACTER VARYING NOT NULL, - payout_tool_info nw.payout_tool_info NOT NULL, - payout_tool_info_russian_bank_account CHARACTER VARYING, - payout_tool_info_russian_bank_name CHARACTER VARYING, - payout_tool_info_russian_bank_post_account CHARACTER VARYING, - payout_tool_info_russian_bank_bik CHARACTER VARYING, - payout_tool_info_international_bank_account_holder CHARACTER VARYING, - payout_tool_info_international_bank_name CHARACTER VARYING, - payout_tool_info_international_bank_address CHARACTER VARYING, - payout_tool_info_international_bank_iban CHARACTER VARYING, - payout_tool_info_international_bank_bic CHARACTER VARYING, - payout_tool_info_international_bank_local_code CHARACTER VARYING, - CONSTRAINT payout_tool_pkey PRIMARY KEY (id), - CONSTRAINT fk_payout_tool_to_contract FOREIGN KEY (cntrct_id) REFERENCES nw.contract(id) -); - -CREATE INDEX payout_tool_idx on nw.payout_tool(cntrct_id); - --- contractor -- - -CREATE TYPE nw.contractor_type AS ENUM ('registered_user', 'legal_entity', 'private_entity'); -CREATE TYPE nw.legal_entity AS ENUM ('russian_legal_entity', 'international_legal_entity'); -CREATE TYPE nw.private_entity AS ENUM ('russian_private_entity'); - -CREATE TABLE nw.contractor( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - party_id CHARACTER VARYING NOT NULL, - contractor_id CHARACTER VARYING NOT NULL, - type nw.contractor_type NOT NULL, - identificational_level CHARACTER VARYING, - registered_user_email CHARACTER VARYING, - legal_entity nw.legal_entity, - russian_legal_entity_registered_name CHARACTER VARYING, - russian_legal_entity_registered_number CHARACTER VARYING, - russian_legal_entity_inn CHARACTER VARYING, - russian_legal_entity_actual_address CHARACTER VARYING, - russian_legal_entity_post_address CHARACTER VARYING, - russian_legal_entity_representative_position CHARACTER VARYING, - russian_legal_entity_representative_full_name CHARACTER VARYING, - russian_legal_entity_representative_document CHARACTER VARYING, - russian_legal_entity_russian_bank_account CHARACTER VARYING, - russian_legal_entity_russian_bank_name CHARACTER VARYING, - russian_legal_entity_russian_bank_post_account CHARACTER VARYING, - russian_legal_entity_russian_bank_bik CHARACTER VARYING, - international_legal_entity_legal_name CHARACTER VARYING, - international_legal_entity_trading_name CHARACTER VARYING, - international_legal_entity_registered_address CHARACTER VARYING, - international_legal_entity_actual_address CHARACTER VARYING, - international_legal_entity_registered_number CHARACTER VARYING, - private_entity nw.private_entity, - russian_private_entity_first_name CHARACTER VARYING, - russian_private_entity_second_name CHARACTER VARYING, - russian_private_entity_middle_name CHARACTER VARYING, - russian_private_entity_phone_number CHARACTER VARYING, - russian_private_entity_email CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT contractor_pkey PRIMARY KEY (id) -); - -CREATE INDEX contractor_event_id on nw.contractor(event_id); -CREATE INDEX contractor_event_created_at on nw.contractor(event_created_at); -CREATE INDEX contractor_contractor_id on nw.contractor(contractor_id); -CREATE INDEX contractor_party_id on nw.contractor(party_id); - --- shop -- - -CREATE TABLE nw.shop( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - party_id CHARACTER VARYING NOT NULL, - shop_id CHARACTER VARYING NOT NULL, - created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - blocking nw.blocking NOT NULL, - blocking_unblocked_reason CHARACTER VARYING, - blocking_unblocked_since TIMESTAMP WITHOUT TIME ZONE, - blocking_blocked_reason CHARACTER VARYING, - blocking_blocked_since TIMESTAMP WITHOUT TIME ZONE, - suspension nw.suspension NOT NULL, - suspension_active_since TIMESTAMP WITHOUT TIME ZONE, - suspension_suspended_since TIMESTAMP WITHOUT TIME ZONE, - details_name CHARACTER VARYING NOT NULL, - details_description CHARACTER VARYING, - location_url CHARACTER VARYING NOT NULL, - category_id INT NOT NULL, - account_currency_code CHARACTER VARYING, - account_settlement BIGINT, - account_guarantee BIGINT, - account_payout BIGINT, - contract_id CHARACTER VARYING NOT NULL, - payout_tool_id CHARACTER VARYING, - payout_schedule_id INT, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT shop_pkey PRIMARY KEY (id) -); - -CREATE INDEX shop_event_id on nw.shop(event_id); -CREATE INDEX shop_event_created_at on nw.shop(event_created_at); -CREATE INDEX shop_shop_id on nw.shop(shop_id); -CREATE INDEX shop_party_id on nw.shop(party_id); -CREATE INDEX shop_created_at on nw.shop(created_at); - --- payout -- - -CREATE TYPE nw.payout_status AS ENUM ('unpaid', 'paid', 'cancelled', 'confirmed'); -CREATE TYPE nw.payout_paid_status_details AS ENUM ('card_details', 'account_details'); -CREATE TYPE nw.user_type AS ENUM ('internal_user', 'external_user', 'service_user'); -CREATE TYPE nw.payout_type AS ENUM ('bank_card', 'bank_account'); -CREATE TYPE nw.payout_account_type AS ENUM ('russian_payout_account', 'international_payout_account'); - -CREATE TABLE nw.payout( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - payout_id CHARACTER VARYING NOT NULL, - party_id CHARACTER VARYING NOT NULL, - shop_id CHARACTER VARYING NOT NULL, - contract_id CHARACTER VARYING NOT NULL, - created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - status nw.payout_status NOT NULL, - status_paid_details nw.payout_paid_status_details, - status_paid_details_card_provider_name CHARACTER VARYING, - status_paid_details_card_provider_transaction_id CHARACTER VARYING, - status_cancelled_user_info_id CHARACTER VARYING, - status_cancelled_user_info_type nw.user_type, - status_cancelled_details CHARACTER VARYING, - status_confirmed_user_info_id CHARACTER VARYING, - status_confirmed_user_info_type nw.user_type, - type nw.payout_type NOT NULL, - type_card_token CHARACTER VARYING, - type_card_payment_system CHARACTER VARYING, - type_card_bin CHARACTER VARYING, - type_card_masked_pan CHARACTER VARYING, - type_card_token_provider CHARACTER VARYING, - type_account_type nw.payout_account_type, - type_account_russian_account CHARACTER VARYING, - type_account_russian_bank_name CHARACTER VARYING, - type_account_russian_bank_post_account CHARACTER VARYING, - type_account_russian_bank_bik CHARACTER VARYING, - type_account_russian_inn CHARACTER VARYING, - type_account_international_account_holder CHARACTER VARYING, - type_account_international_bank_name CHARACTER VARYING, - type_account_international_bank_address CHARACTER VARYING, - type_account_international_iban CHARACTER VARYING, - type_account_international_bic CHARACTER VARYING, - type_account_international_local_bank_code CHARACTER VARYING, - type_account_international_legal_entity_legal_name CHARACTER VARYING, - type_account_international_legal_entity_trading_name CHARACTER VARYING, - type_account_international_legal_entity_registered_address CHARACTER VARYING, - type_account_international_legal_entity_actual_address CHARACTER VARYING, - type_account_international_legal_entity_registered_number CHARACTER VARYING, - type_account_purpose CHARACTER VARYING, - type_account_legal_agreement_signed_at TIMESTAMP WITHOUT TIME ZONE, - type_account_legal_agreement_id CHARACTER VARYING, - type_account_legal_agreement_valid_until TIMESTAMP WITHOUT TIME ZONE, - initiator_id CHARACTER VARYING NOT NULL, - initiator_type nw.user_type NOT NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT payout_pkey PRIMARY KEY (id) -); - -CREATE INDEX payout_event_id on nw.payout(event_id); -CREATE INDEX payout_event_created_at on nw.payout(event_created_at); -CREATE INDEX payout_payout_id on nw.payout(payout_id); -CREATE INDEX payout_party_id on nw.payout(party_id); -CREATE INDEX payout_created_at on nw.payout(created_at); -CREATE INDEX payout_status on nw.payout(status); - -CREATE TABLE nw.payout_summary( - id BIGSERIAL NOT NULL, - pyt_id BIGINT NOT NULL, - amount BIGINT NOT NULL, - fee BIGINT NOT NULL, - currency_code CHARACTER VARYING NOT NULL, - from_time TIMESTAMP WITHOUT TIME ZONE NOT NULL, - to_time TIMESTAMP WITHOUT TIME ZONE NOT NULL, - operation_type CHARACTER VARYING NOT NULL, - count INT NOT NULL, - CONSTRAINT payout_summary_pkey PRIMARY KEY (id), - CONSTRAINT fk_summary_to_payout FOREIGN KEY (pyt_id) REFERENCES nw.payout(id) -); - -CREATE INDEX payout_summary_idx on nw.payout_summary(pyt_id); \ No newline at end of file +CREATE SCHEMA IF NOT EXISTS dw; + +CREATE TYPE dw.adjustment_cash_flow_type AS ENUM ( + 'new_cash_flow', + 'old_cash_flow_inverse' + ); + +CREATE TYPE dw.adjustment_status AS ENUM ( + 'pending', + 'captured', + 'cancelled', + 'processed' + ); + +CREATE TYPE dw.bank_card_payment_system AS ENUM ( + 'visa', + 'mastercard', + 'visaelectron', + 'maestro', + 'forbrugsforeningen', + 'dankort', + 'amex', + 'dinersclub', + 'discover', + 'unionpay', + 'jcb', + 'nspkmir', + 'elo', + 'rupay', + 'ebt', + 'uzcard' + ); + +CREATE TYPE dw.blocking AS ENUM ( + 'unblocked', + 'blocked' + ); + +CREATE TYPE dw.cash_flow_account AS ENUM ( + 'merchant', + 'provider', + 'system', + 'external', + 'wallet' + ); + +CREATE TYPE dw.challenge_resolution AS ENUM ( + 'approved', + 'denied' + ); + +CREATE TYPE dw.challenge_status AS ENUM ( + 'pending', + 'cancelled', + 'completed', + 'failed' + ); + +CREATE TYPE dw.chargeback_category AS ENUM ( + 'fraud', + 'dispute', + 'authorisation', + 'processing_error' + ); + +CREATE TYPE dw.chargeback_stage AS ENUM ( + 'chargeback', + 'pre_arbitration', + 'arbitration' + ); + +CREATE TYPE dw.chargeback_status AS ENUM ( + 'pending', + 'accepted', + 'rejected', + 'cancelled' + ); + +CREATE TYPE dw.contract_status AS ENUM ( + 'active', + 'terminated', + 'expired' + ); + +CREATE TYPE dw.contractor_type AS ENUM ( + 'registered_user', + 'legal_entity', + 'private_entity' + ); + +CREATE TYPE dw.deposit_adjustment_status AS ENUM ( + 'pending', + 'succeeded' + ); + +CREATE TYPE dw.deposit_revert_status AS ENUM ( + 'pending', + 'succeeded', + 'failed' + ); + +CREATE TYPE dw.deposit_status AS ENUM ( + 'pending', + 'succeeded', + 'failed' + ); + +CREATE TYPE dw.deposit_transfer_status AS ENUM ( + 'created', + 'prepared', + 'committed', + 'cancelled' + ); + +CREATE TYPE dw.destination_resource_type AS ENUM ( + 'bank_card', + 'crypto_wallet', + 'digital_wallet', + 'generic' + ); + +CREATE TYPE dw.destination_status AS ENUM ( + 'authorized', + 'unauthorized' + ); + +CREATE TYPE dw.fistful_cash_flow_change_type AS ENUM ( + 'withdrawal', + 'deposit', + 'deposit_revert', + 'deposit_adjustment' + ); + +CREATE TYPE dw.invoice_status AS ENUM ( + 'unpaid', + 'paid', + 'cancelled', + 'fulfilled' + ); + +CREATE TYPE dw.legal_entity AS ENUM ( + 'russian_legal_entity', + 'international_legal_entity' + ); + +CREATE TYPE dw.mobile_operator_type AS ENUM ( + 'mts', + 'beeline', + 'megafone', + 'tele2', + 'yota' + ); + +CREATE TYPE dw.payer_type AS ENUM ( + 'payment_resource', + 'customer', + 'recurrent' + ); + +CREATE TYPE dw.payment_change_type AS ENUM ( + 'payment', + 'refund', + 'adjustment', + 'payout', + 'chargeback' + ); + +CREATE TYPE dw.payment_flow_type AS ENUM ( + 'instant', + 'hold' + ); + +CREATE TYPE dw.payment_method_type AS ENUM ( + 'bank_card', + 'payment_terminal', + 'digital_wallet', + 'tokenized_bank_card', + 'empty_cvv_bank_card', + 'crypto_currency', + 'mobile', + 'generic' + ); + +CREATE TYPE dw.payment_status AS ENUM ( + 'pending', + 'processed', + 'captured', + 'cancelled', + 'refunded', + 'failed', + 'charged_back' + ); + +CREATE TYPE dw.payment_tool_type AS ENUM ( + 'bank_card', + 'payment_terminal', + 'digital_wallet', + 'crypto_currency', + 'mobile_commerce', + 'crypto_currency_deprecated' + ); + +CREATE TYPE dw.payout_account_type AS ENUM ( + 'russian_payout_account', + 'international_payout_account' + ); + +CREATE TYPE dw.payout_paid_status_details AS ENUM ( + 'card_details', + 'account_details' + ); + +CREATE TYPE dw.payout_status AS ENUM ( + 'unpaid', + 'paid', + 'cancelled', + 'confirmed' + ); + +CREATE TYPE dw.payout_tool_info AS ENUM ( + 'russian_bank_account', + 'international_bank_account', + 'wallet_info', + 'payment_institution_account' + ); + +CREATE TYPE dw.payout_type AS ENUM ( + 'bank_card', + 'bank_account', + 'wallet' + ); + +CREATE TYPE dw.private_entity AS ENUM ( + 'russian_private_entity' + ); + +CREATE TYPE dw.recurrent_payment_tool_status AS ENUM ( + 'created', + 'acquired', + 'abandoned', + 'failed' + ); + +CREATE TYPE dw.refund_status AS ENUM ( + 'pending', + 'succeeded', + 'failed' + ); + +CREATE TYPE dw.representative_document AS ENUM ( + 'articles_of_association', + 'power_of_attorney', + 'expired' + ); + +CREATE TYPE dw.risk_score AS ENUM ( + 'low', + 'high', + 'fatal' + ); + +CREATE TYPE dw.source_status AS ENUM ( + 'authorized', + 'unauthorized' + ); + +CREATE TYPE dw.suspension AS ENUM ( + 'active', + 'suspended' + ); + +CREATE TYPE dw.user_type AS ENUM ( + 'internal_user', + 'external_user', + 'service_user' + ); + +CREATE TYPE dw.withdrawal_session_status AS ENUM ( + 'active', + 'success', + 'failed' + ); + +CREATE TYPE dw.withdrawal_status AS ENUM ( + 'pending', + 'succeeded', + 'failed' + ); + +CREATE TYPE dw.withdrawal_transfer_status AS ENUM ( + 'created', + 'prepared', + 'committed', + 'cancelled' + ); + + +-- TABLES +CREATE TABLE dw.adjustment +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + domain_revision bigint NOT NULL, + adjustment_id character varying NOT NULL, + payment_id character varying NOT NULL, + invoice_id character varying NOT NULL, + party_id character varying NOT NULL, + shop_id character varying NOT NULL, + created_at timestamp without time zone NOT NULL, + status dw.adjustment_status NOT NULL, + status_captured_at timestamp without time zone, + status_cancelled_at timestamp without time zone, + reason character varying NOT NULL, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + party_revision bigint, + sequence_id bigint, + change_id integer, + payment_status dw.payment_status, + amount bigint NOT NULL, + provider_amount_diff bigint DEFAULT 0, + system_amount_diff bigint DEFAULT 0, + external_income_amount_diff bigint DEFAULT 0, + external_outcome_amount_diff bigint DEFAULT 0, + CONSTRAINT adjustment_pkey PRIMARY KEY (id), + CONSTRAINT adjustment_uniq UNIQUE (invoice_id, sequence_id, change_id) +); + +CREATE INDEX adjustment_created_at ON dw.adjustment USING btree (created_at); +CREATE INDEX adjustment_event_created_at ON dw.adjustment USING btree (event_created_at); +CREATE INDEX adjustment_invoice_id ON dw.adjustment USING btree (invoice_id); +CREATE INDEX adjustment_party_id ON dw.adjustment USING btree (party_id); +CREATE INDEX adjustment_status ON dw.adjustment USING btree (status); + + +create table dw.cash_flow_link +( + id BIGSERIAL NOT NULL, + event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + invoice_id CHARACTER VARYING NOT NULL, + payment_id CHARACTER VARYING NOT NULL, + sequence_id BIGINT, + change_id INTEGER, + wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + current BOOLEAN NOT NULL DEFAULT false, + CONSTRAINT cash_flow_link_pkey PRIMARY KEY (id), + CONSTRAINT cash_flow_link_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id) +); + + +CREATE TABLE dw.cash_flow +( + id bigserial NOT NULL, + obj_id bigint NOT NULL, + obj_type dw.payment_change_type NOT NULL, + adj_flow_type dw.adjustment_cash_flow_type, + source_account_type dw.cash_flow_account NOT NULL, + source_account_type_value character varying NOT NULL, + source_account_id bigint NOT NULL, + destination_account_type dw.cash_flow_account NOT NULL, + destination_account_type_value character varying NOT NULL, + destination_account_id bigint NOT NULL, + amount bigint NOT NULL, + currency_code character varying NOT NULL, + details character varying, + CONSTRAINT cash_flow_pkey PRIMARY KEY (id) +); + +CREATE INDEX cash_flow_idx ON dw.cash_flow USING btree (obj_id, obj_type); + + +CREATE TABLE dw.calendar +( + id bigserial NOT NULL, + version_id bigint NOT NULL, + calendar_ref_id integer NOT NULL, + name character varying NOT NULL, + description character varying, + timezone character varying NOT NULL, + holidays_json character varying NOT NULL, + first_day_of_week integer, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + CONSTRAINT calendar_pkey PRIMARY KEY (id) +); + +CREATE INDEX calendar_idx ON dw.calendar USING btree (calendar_ref_id); +CREATE INDEX calendar_version_id ON dw.calendar USING btree (version_id); + + +CREATE TABLE dw.category +( + id bigserial NOT NULL, + version_id bigint NOT NULL, + category_ref_id integer NOT NULL, + name character varying NOT NULL, + description character varying NOT NULL, + type character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + CONSTRAINT category_pkey PRIMARY KEY (id) +); + +CREATE INDEX category_idx ON dw.category USING btree (category_ref_id); +CREATE INDEX category_version_id ON dw.category USING btree (version_id); + + +CREATE TABLE dw.challenge +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + event_occured_at timestamp without time zone NOT NULL, + sequence_id integer NOT NULL, + identity_id character varying NOT NULL, + challenge_id character varying NOT NULL, + challenge_class_id character varying NOT NULL, + challenge_status dw.challenge_status NOT NULL, + challenge_resolution dw.challenge_resolution, + challenge_valid_until timestamp without time zone, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + proofs_json character varying, + CONSTRAINT challenge_pkey PRIMARY KEY (id), + CONSTRAINT challenge_uniq UNIQUE (challenge_id, identity_id, sequence_id) +); + +CREATE INDEX challenge_event_created_at_idx ON dw.challenge USING btree (event_created_at); +CREATE INDEX challenge_event_occured_at_idx ON dw.challenge USING btree (event_occured_at); +CREATE INDEX challenge_id_idx ON dw.challenge USING btree (challenge_id); + + +CREATE TABLE dw.chargeback +( + id bigserial NOT NULL, + sequence_id bigint NOT NULL, + change_id integer NOT NULL, + domain_revision bigint NOT NULL, + party_revision bigint, + chargeback_id character varying NOT NULL, + payment_id character varying NOT NULL, + invoice_id character varying NOT NULL, + shop_id character varying NOT NULL, + party_id character varying NOT NULL, + external_id character varying, + event_created_at timestamp without time zone NOT NULL, + created_at timestamp without time zone NOT NULL, + status dw.chargeback_status NOT NULL, + levy_amount bigint, + levy_currency_code character varying, + amount bigint, + currency_code character varying, + reason_code character varying, + reason_category dw.chargeback_category NOT NULL, + stage dw.chargeback_stage NOT NULL, + current boolean DEFAULT true NOT NULL, + context bytea, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + CONSTRAINT chargeback_pkey PRIMARY KEY (id), + CONSTRAINT chargeback_uniq UNIQUE (invoice_id, sequence_id, change_id) +); + +CREATE INDEX chargeback_created_at ON dw.chargeback USING btree (created_at); +CREATE INDEX chargeback_event_created_at ON dw.chargeback USING btree (event_created_at); +CREATE INDEX chargeback_invoice_id ON dw.chargeback USING btree (invoice_id); +CREATE INDEX chargeback_party_id ON dw.chargeback USING btree (party_id); +CREATE INDEX chargeback_status ON dw.chargeback USING btree (status); + + +CREATE TABLE dw.contract +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + contract_id character varying NOT NULL, + party_id character varying NOT NULL, + payment_institution_id integer, + created_at timestamp without time zone NOT NULL, + valid_since timestamp without time zone, + valid_until timestamp without time zone, + status dw.contract_status NOT NULL, + status_terminated_at timestamp without time zone, + terms_id integer NOT NULL, + legal_agreement_signed_at timestamp without time zone, + legal_agreement_id character varying, + legal_agreement_valid_until timestamp without time zone, + report_act_schedule_id integer, + report_act_signer_position character varying, + report_act_signer_full_name character varying, + report_act_signer_document dw.representative_document, + report_act_signer_doc_power_of_attorney_signed_at timestamp without time zone, + report_act_signer_doc_power_of_attorney_legal_agreement_id character varying, + report_act_signer_doc_power_of_attorney_valid_until timestamp without time zone, + contractor_id character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + sequence_id integer, + change_id integer, + claim_effect_id integer, + CONSTRAINT contract_pkey PRIMARY KEY (id), + CONSTRAINT contract_uniq UNIQUE (party_id, contract_id, sequence_id, change_id, claim_effect_id) +); + +CREATE INDEX contract_contract_id ON dw.contract USING btree (contract_id); +CREATE INDEX contract_created_at ON dw.contract USING btree (created_at); +CREATE INDEX contract_event_created_at ON dw.contract USING btree (event_created_at); +CREATE INDEX contract_party_id ON dw.contract USING btree (party_id); + + +CREATE TABLE dw.contract_adjustment +( + id bigserial NOT NULL, + cntrct_id bigint NOT NULL, + contract_adjustment_id character varying NOT NULL, + created_at timestamp without time zone NOT NULL, + valid_since timestamp without time zone, + valid_until timestamp without time zone, + terms_id integer NOT NULL, + CONSTRAINT contract_adjustment_pkey PRIMARY KEY (id) +); + +CREATE INDEX contract_adjustment_idx ON dw.contract_adjustment USING btree (cntrct_id); + + +CREATE TABLE dw.contract_revision +( + id bigserial NOT NULL, + obj_id bigint NOT NULL, + revision bigint NOT NULL, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + CONSTRAINT contract_revision_pkey PRIMARY KEY (id) +); + +CREATE UNIQUE INDEX contract_revision_idx ON dw.contract_revision USING btree (obj_id, revision); + + +CREATE TABLE dw.contractor +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + party_id character varying NOT NULL, + contractor_id character varying NOT NULL, + type dw.contractor_type NOT NULL, + identificational_level character varying, + registered_user_email character varying, + legal_entity dw.legal_entity, + russian_legal_entity_registered_name character varying, + russian_legal_entity_registered_number character varying, + russian_legal_entity_inn character varying, + russian_legal_entity_actual_address character varying, + russian_legal_entity_post_address character varying, + russian_legal_entity_representative_position character varying, + russian_legal_entity_representative_full_name character varying, + russian_legal_entity_representative_document character varying, + russian_legal_entity_russian_bank_account character varying, + russian_legal_entity_russian_bank_name character varying, + russian_legal_entity_russian_bank_post_account character varying, + russian_legal_entity_russian_bank_bik character varying, + international_legal_entity_legal_name character varying, + international_legal_entity_trading_name character varying, + international_legal_entity_registered_address character varying, + international_legal_entity_actual_address character varying, + international_legal_entity_registered_number character varying, + private_entity dw.private_entity, + russian_private_entity_first_name character varying, + russian_private_entity_second_name character varying, + russian_private_entity_middle_name character varying, + russian_private_entity_phone_number character varying, + russian_private_entity_email character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + sequence_id integer, + change_id integer, + claim_effect_id integer, + international_legal_entity_country_code character varying, + CONSTRAINT contractor_pkey PRIMARY KEY (id), + CONSTRAINT contractor_uniq UNIQUE (party_id, contractor_id, sequence_id, change_id, claim_effect_id) +); + +CREATE INDEX contractor_contractor_id ON dw.contractor USING btree (contractor_id); +CREATE INDEX contractor_event_created_at ON dw.contractor USING btree (event_created_at); +CREATE INDEX contractor_party_id ON dw.contractor USING btree (party_id); + + +CREATE TABLE dw.contractor_revision +( + id bigserial NOT NULL, + obj_id bigint NOT NULL, + revision bigint NOT NULL, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + CONSTRAINT contractor_revision_pkey PRIMARY KEY (id) +); + +CREATE UNIQUE INDEX contractor_revision_idx ON dw.contractor_revision USING btree (obj_id, revision); + + +CREATE TABLE dw.country +( + id bigserial NOT NULL, + version_id bigint NOT NULL, + country_ref_id character varying NOT NULL, + name character varying NOT NULL, + trade_bloc text[] NOT NULL, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + CONSTRAINT country_pkey PRIMARY KEY (id) +); + + + +CREATE TABLE dw.currency +( + id bigserial NOT NULL, + version_id bigint NOT NULL, + currency_ref_id character varying NOT NULL, + name character varying NOT NULL, + symbolic_code character varying NOT NULL, + numeric_code smallint NOT NULL, + exponent smallint NOT NULL, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + CONSTRAINT currency_pkey PRIMARY KEY (id) +); + +CREATE INDEX currency_idx ON dw.currency USING btree (currency_ref_id); +CREATE INDEX currency_version_id ON dw.currency USING btree (version_id); + + +CREATE TABLE dw.deposit +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + event_occured_at timestamp without time zone NOT NULL, + sequence_id integer NOT NULL, + source_id character varying NOT NULL, + wallet_id character varying NOT NULL, + deposit_id character varying NOT NULL, + amount bigint NOT NULL, + fee bigint, + provider_fee bigint, + currency_code character varying NOT NULL, + deposit_status dw.deposit_status NOT NULL, + deposit_transfer_status dw.deposit_transfer_status, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + external_id character varying, + CONSTRAINT deposit_pkey PRIMARY KEY (id), + CONSTRAINT deposit_uniq UNIQUE (deposit_id, sequence_id) +); + +CREATE INDEX deposit_event_created_at_idx ON dw.deposit USING btree (event_created_at); +CREATE INDEX deposit_event_occured_at_idx ON dw.deposit USING btree (event_occured_at); +CREATE INDEX deposit_id_idx ON dw.deposit USING btree (deposit_id); + + +CREATE TABLE dw.deposit_adjustment +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + event_occured_at timestamp without time zone NOT NULL, + sequence_id integer NOT NULL, + source_id character varying NOT NULL, + wallet_id character varying NOT NULL, + deposit_id character varying NOT NULL, + adjustment_id character varying NOT NULL, + amount bigint, + fee bigint, + provider_fee bigint, + currency_code character varying, + status dw.deposit_adjustment_status NOT NULL, + transfer_status dw.deposit_transfer_status, + deposit_status dw.deposit_status, + external_id character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + party_revision bigint DEFAULT 0 NOT NULL, + domain_revision bigint DEFAULT 0 NOT NULL, + CONSTRAINT deposit_adjustment_pkey PRIMARY KEY (id), + CONSTRAINT deposit_adjustment_uniq UNIQUE (deposit_id, adjustment_id, sequence_id) +); + +CREATE INDEX deposit_adjustment_event_created_at_idx ON dw.deposit_adjustment USING btree (event_created_at); +CREATE INDEX deposit_adjustment_id_idx ON dw.deposit_adjustment USING btree (deposit_id); + + +CREATE TABLE dw.deposit_revert +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + event_occured_at timestamp without time zone NOT NULL, + sequence_id integer NOT NULL, + source_id character varying NOT NULL, + wallet_id character varying NOT NULL, + deposit_id character varying NOT NULL, + revert_id character varying NOT NULL, + amount bigint NOT NULL, + fee bigint, + provider_fee bigint, + currency_code character varying NOT NULL, + status dw.deposit_revert_status NOT NULL, + transfer_status dw.deposit_transfer_status, + reason character varying, + external_id character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + party_revision bigint DEFAULT 0 NOT NULL, + domain_revision bigint DEFAULT 0 NOT NULL, + CONSTRAINT deposit_revert_pkey PRIMARY KEY (id), + CONSTRAINT deposit_revert_uniq UNIQUE (deposit_id, revert_id, sequence_id) +); + +CREATE INDEX deposit_revert_event_created_at_idx ON dw.deposit_revert USING btree (event_created_at); +CREATE INDEX deposit_revert_id_idx ON dw.deposit_revert USING btree (deposit_id); + + +CREATE TABLE dw.destination +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + event_occured_at timestamp without time zone NOT NULL, + sequence_id integer NOT NULL, + destination_id character varying NOT NULL, + destination_name character varying NOT NULL, + destination_status dw.destination_status NOT NULL, + resource_bank_card_token character varying, + resource_bank_card_payment_system character varying, + resource_bank_card_bin character varying, + resource_bank_card_masked_pan character varying, + account_id character varying, + identity_id character varying, + party_id character varying, + accounter_account_id bigint, + currency_code character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + external_id character varying, + created_at timestamp without time zone, + context_json character varying, + resource_crypto_wallet_id character varying, + resource_crypto_wallet_type character varying, + resource_type dw.destination_resource_type NOT NULL, + resource_crypto_wallet_data character varying, + resource_bank_card_type character varying, + resource_bank_card_issuer_country character varying, + resource_bank_card_bank_name character varying, + resource_digital_wallet_id character varying, + resource_digital_wallet_data character varying, + CONSTRAINT destination_pkey PRIMARY KEY (id), + CONSTRAINT destination_uniq UNIQUE (destination_id, sequence_id) +); + +CREATE INDEX destination_event_created_at_idx ON dw.destination USING btree (event_created_at); +CREATE INDEX destination_event_occured_at_idx ON dw.destination USING btree (event_occured_at); +CREATE INDEX destination_id_idx ON dw.destination USING btree (destination_id); + + +CREATE TABLE dw.fistful_cash_flow +( + id bigserial NOT NULL, + obj_id bigint NOT NULL, + source_account_type dw.cash_flow_account NOT NULL, + source_account_type_value character varying NOT NULL, + source_account_id character varying NOT NULL, + destination_account_type dw.cash_flow_account NOT NULL, + destination_account_type_value character varying NOT NULL, + destination_account_id character varying NOT NULL, + amount bigint NOT NULL, + currency_code character varying NOT NULL, + details character varying, + obj_type dw.fistful_cash_flow_change_type NOT NULL, + CONSTRAINT fistful_cash_flow_pkey PRIMARY KEY (id) +); + +CREATE INDEX fistful_cash_flow_obj_id_idx ON dw.fistful_cash_flow USING btree (obj_id); + + +CREATE TABLE dw.identity +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + event_occured_at timestamp without time zone NOT NULL, + sequence_id integer NOT NULL, + party_id character varying NOT NULL, + party_contract_id character varying, + identity_id character varying NOT NULL, + identity_provider_id character varying NOT NULL, + identity_effective_chalenge_id character varying, + identity_level_id character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + external_id character varying, + blocked boolean, + context_json character varying, + CONSTRAINT identity_pkey PRIMARY KEY (id), + CONSTRAINT identity_uniq UNIQUE (identity_id, sequence_id) +); + +CREATE INDEX identity_event_created_at_idx ON dw.identity USING btree (event_created_at); +CREATE INDEX identity_event_occured_at_idx ON dw.identity USING btree (event_occured_at); +CREATE INDEX identity_id_idx ON dw.identity USING btree (identity_id); +CREATE INDEX identity_party_id_idx ON dw.identity USING btree (party_id); + + +CREATE TABLE dw.inspector +( + id bigserial NOT NULL, + version_id bigint NOT NULL, + inspector_ref_id integer NOT NULL, + name character varying NOT NULL, + description character varying NOT NULL, + proxy_ref_id integer NOT NULL, + proxy_additional_json character varying NOT NULL, + fallback_risk_score character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + CONSTRAINT inspector_pkey PRIMARY KEY (id) +); + +CREATE INDEX inspector_idx ON dw.inspector USING btree (inspector_ref_id); +CREATE INDEX inspector_version_id ON dw.inspector USING btree (version_id); + + +CREATE TABLE IF NOT EXISTS dw.invoice +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + invoice_id character varying COLLATE pg_catalog."default" NOT NULL, + party_id character varying COLLATE pg_catalog."default" NOT NULL, + shop_id character varying COLLATE pg_catalog."default" NOT NULL, + party_revision bigint, + created_at timestamp without time zone NOT NULL, + + details_product character varying COLLATE pg_catalog."default" NOT NULL, + details_description character varying COLLATE pg_catalog."default", + due timestamp without time zone NOT NULL, + amount bigint NOT NULL, + currency_code character varying COLLATE pg_catalog."default" NOT NULL, + context bytea, + template_id character varying COLLATE pg_catalog."default", + wtime timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + sequence_id bigint, + change_id integer, + external_id character varying COLLATE pg_catalog."default", + CONSTRAINT invoice_pkey PRIMARY KEY (id), + CONSTRAINT invoice_uniq UNIQUE (invoice_id, sequence_id, change_id) +); + +-- TODO refactor indices +CREATE INDEX invoice_created_at ON dw.invoice USING btree (created_at); +CREATE INDEX invoice_event_created_at ON dw.invoice USING btree (event_created_at); +CREATE INDEX invoice_external_id_idx ON dw.invoice USING btree (external_id) WHERE (external_id IS NOT NULL); +CREATE INDEX invoice_invoice_id ON dw.invoice USING btree (invoice_id); +CREATE INDEX invoice_party_id ON dw.invoice USING btree (party_id); + +CREATE TABLE IF NOT EXISTS dw.invoice_status_info +( + id BIGSERIAL, + event_created_at timestamp without time zone NOT NULL, + invoice_id character varying COLLATE pg_catalog."default" NOT NULL, + status dw.invoice_status NOT NULL, + details character varying COLLATE pg_catalog."default", + wtime timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + current boolean DEFAULT false NOT NULL, + sequence_id bigint, + change_id integer, + external_id character varying COLLATE pg_catalog."default", + CONSTRAINT invoice_status_pkey PRIMARY KEY (id), + CONSTRAINT invoice_status_uniq UNIQUE (invoice_id, sequence_id, change_id) +); + +CREATE INDEX invoice_status ON dw.invoice_status_info USING btree (status); + + +CREATE TABLE IF NOT EXISTS dw.invoice_cart +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + invoice_id character varying COLLATE pg_catalog."default" NOT NULL, + product character varying COLLATE pg_catalog."default", + quantity integer NOT NULL, + amount bigint NOT NULL, + currency_code character varying COLLATE pg_catalog."default" NOT NULL, + metadata_json character varying COLLATE pg_catalog."default" NOT NULL, + wtime timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + sequence_id bigint, + change_id integer, + CONSTRAINT invoice_cart_pkey PRIMARY KEY (id) +); + +CREATE INDEX invoice_cart_invoice_id ON dw.invoice_cart USING btree (invoice_id); + + +CREATE TABLE dw.party +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + party_id character varying NOT NULL, + contact_info_email character varying NOT NULL, + created_at timestamp without time zone NOT NULL, + blocking dw.blocking NOT NULL, + blocking_unblocked_reason character varying, + blocking_unblocked_since timestamp without time zone, + blocking_blocked_reason character varying, + blocking_blocked_since timestamp without time zone, + suspension dw.suspension NOT NULL, + suspension_active_since timestamp without time zone, + suspension_suspended_since timestamp without time zone, + revision bigint NOT NULL, + revision_changed_at timestamp without time zone, + party_meta_set_ns character varying, + party_meta_set_data_json character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + sequence_id integer, + change_id integer, + CONSTRAINT party_pkey PRIMARY KEY (id), + CONSTRAINT party_uniq UNIQUE (party_id, sequence_id, change_id) +); + +CREATE INDEX party_contact_info_email ON dw.party USING btree (contact_info_email); +CREATE INDEX party_created_at ON dw.party USING btree (created_at); +CREATE INDEX party_current ON dw.party USING btree (current); +CREATE INDEX party_event_created_at ON dw.party USING btree (event_created_at); +CREATE INDEX party_party_id ON dw.party USING btree (party_id); + +CREATE TABLE IF NOT EXISTS dw.payment +( + id BIGSERIAL NOT NULL, + event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + invoice_id CHARACTER VARYING NOT NULL, + payment_id CHARACTER VARYING NOT NULL, + created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + party_id CHARACTER VARYING NOT NULL, + shop_id CHARACTER VARYING NOT NULL, + domain_revision BIGINT NOT NULL, + party_revision BIGINT, + amount BIGINT NOT NULL, + currency_code CHARACTER VARYING NOT NULL, + make_recurrent BOOLEAN, + sequence_id BIGINT, + change_id INTEGER, + wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + external_id character varying COLLATE pg_catalog."default", + payment_flow_type dw.payment_flow_type NOT NULL, + payment_flow_on_hold_expiration character varying COLLATE pg_catalog."default", + payment_flow_held_until timestamp without time zone, + + CONSTRAINT payment_pkey PRIMARY KEY (id), + CONSTRAINT payment_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id) +); + +-- TODO refactor indices +CREATE INDEX payment_created_at ON dw.payment USING btree (created_at); +CREATE INDEX payment_event_created_at ON dw.payment USING btree (event_created_at); +CREATE INDEX payment_external_id_idx ON dw.payment USING btree (external_id) WHERE (external_id IS NOT NULL); +CREATE INDEX payment_invoice_id ON dw.payment USING btree (invoice_id); +CREATE INDEX payment_party_id ON dw.payment USING btree (party_id); + +CREATE TABLE IF NOT EXISTS dw.payment_fee +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + invoice_id character varying NOT NULL, + payment_id character varying NOT NULL, + fee BIGINT, + provider_fee BIGINT, + external_fee BIGINT, + guarantee_deposit BIGINT, + current BOOLEAN NOT NULL DEFAULT false, + wtime timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + sequence_id bigint, + change_id integer, + CONSTRAINT payment_fee_pkey PRIMARY KEY (id), + CONSTRAINT payment_fee_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id) +); + + +CREATE TABLE IF NOT EXISTS dw.payment_route +( + id BIGSERIAL NOT NULL, + event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + invoice_id CHARACTER VARYING NOT NULL, + payment_id CHARACTER VARYING NOT NULL, + route_provider_id INTEGER, + route_terminal_id INTEGER, + sequence_id BIGINT, + change_id INTEGER, + wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + current BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT payment_route_pkey PRIMARY KEY (id), + CONSTRAINT payment_route_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id) +); + + +CREATE TABLE IF NOT EXISTS dw.payment_status_info +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + invoice_id character varying NOT NULL, + payment_id character varying NOT NULL, + status dw.payment_status NOT NULL, + reason character varying, + amount BIGINT, + currency_code CHARACTER VARYING, + cart_json CHARACTER VARYING, + current BOOLEAN NOT NULL DEFAULT false, + wtime timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + sequence_id bigint, + change_id integer, + CONSTRAINT payment_status_pkey PRIMARY KEY (id), + CONSTRAINT payment_status_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id) +); + +CREATE INDEX payment_status ON dw.payment_status_info USING btree (status); + + +CREATE TABLE IF NOT EXISTS dw.payment_payer_info +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + invoice_id character varying, + payment_id character varying, + payer_type dw.payer_type NOT NULL, + payment_tool_type dw.payment_tool_type NOT NULL, + bank_card_token character varying, + bank_card_payment_system character varying, + bank_card_bin character varying, + bank_card_masked_pan character varying, + bank_card_token_provider character varying, + payment_terminal_type character varying, + digital_wallet_provider character varying, + digital_wallet_id character varying, + payment_session_id character varying, + ip_address character varying, + fingerprint character varying, + phone_number character varying, + email character varying, + customer_id character varying, + customer_binding_id character varying, + customer_rec_payment_tool_id character varying, + + recurrent_parent_invoice_id character varying, + recurrent_parent_payment_id character varying, + + crypto_currency_type character varying, + mobile_phone_cc character varying, + mobile_phone_ctn character varying, + issuer_country character varying, + bank_name character varying, + bank_card_cardholder_name character varying, + mobile_operator character varying, + + wtime timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + sequence_id bigint, + change_id integer, + CONSTRAINT payment_payment_payer_info_pkey PRIMARY KEY (id), + CONSTRAINT payment_payment_payer_info_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id) +); + + +CREATE TABLE IF NOT EXISTS dw.payment_additional_info +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + invoice_id character varying NOT NULL, + payment_id character varying NOT NULL, + transaction_id character varying, + extra_json character varying, + rrn character varying, + approval_code character varying, + acs_url character varying, + md character varying, + term_url character varying, + eci character varying, + cavv character varying, + xid character varying, + cavv_algorithm character varying, + three_ds_verification character varying, + current boolean NOT NULL DEFAULT false, + wtime timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + sequence_id bigint, + change_id integer, + CONSTRAINT payment_additional_info_pkey PRIMARY KEY (id), + CONSTRAINT payment_additional_info_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id) +); + + +CREATE TABLE IF NOT EXISTS dw.payment_recurrent_info +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + invoice_id character varying NOT NULL, + payment_id character varying NOT NULL, + + token character varying, + current BOOLEAN NOT NULL DEFAULT false, + wtime timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + sequence_id bigint, + change_id integer, + CONSTRAINT payment_recurrent_info_pkey PRIMARY KEY (id), + CONSTRAINT payment_recurrent_info_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id) +); + +CREATE TABLE IF NOT EXISTS dw.payment_risk_data +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + invoice_id character varying NOT NULL, + payment_id character varying NOT NULL, + risk_score dw.risk_score NOT NULL, + current BOOLEAN NOT NULL DEFAULT false, + wtime timestamp without time zone NOT NULL DEFAULT (now() AT TIME ZONE 'utc'::text), + sequence_id bigint, + change_id integer, + CONSTRAINT payment_risk_data_pkey PRIMARY KEY (id), + CONSTRAINT payment_risk_data_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id) +); + + +CREATE TABLE dw.payment_institution +( + id bigserial NOT NULL, + version_id bigint NOT NULL, + payment_institution_ref_id integer NOT NULL, + name character varying NOT NULL, + description character varying, + calendar_ref_id integer, + system_account_set_json character varying NOT NULL, + default_contract_template_json character varying NOT NULL, + default_wallet_contract_template_json character varying, + providers_json character varying, + inspector_json character varying NOT NULL, + realm character varying NOT NULL, + residences_json character varying NOT NULL, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + CONSTRAINT payment_institution_pkey PRIMARY KEY (id) +); + +CREATE INDEX payment_institution_idx ON dw.payment_institution USING btree (payment_institution_ref_id); +CREATE INDEX payment_institution_version_id ON dw.payment_institution USING btree (version_id); + + +CREATE TABLE dw.payment_method +( + id bigserial NOT NULL, + version_id bigint NOT NULL, + payment_method_ref_id character varying NOT NULL, + name character varying NOT NULL, + description character varying NOT NULL, + type dw.payment_method_type NOT NULL, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + CONSTRAINT payment_method_pkey PRIMARY KEY (id) +); + +CREATE INDEX payment_method_idx ON dw.payment_method USING btree (payment_method_ref_id); +CREATE INDEX payment_method_version_id ON dw.payment_method USING btree (version_id); + + +CREATE TABLE dw.payment_routing_rule +( + id bigserial NOT NULL, + rule_ref_id integer NOT NULL, + name character varying NOT NULL, + description character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + routing_decisions_json character varying NOT NULL, + version_id bigint NOT NULL, + CONSTRAINT payment_routing_rule_pkey PRIMARY KEY (id) +); + +CREATE INDEX payment_routing_rule_ref_id ON dw.payment_routing_rule USING btree (rule_ref_id); + +CREATE TABLE dw.payout +( + id bigserial NOT NULL, + payout_id character varying NOT NULL, + event_created_at timestamp without time zone NOT NULL, + sequence_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + party_id character varying NOT NULL, + shop_id character varying NOT NULL, + status dw.payout_status NOT NULL, + payout_tool_id character varying NOT NULL, + amount bigint NOT NULL, + fee bigint DEFAULT 0 NOT NULL, + currency_code character varying NOT NULL, + cancelled_details character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + CONSTRAINT payout_id_pkey PRIMARY KEY (id), + CONSTRAINT payout_payout_id_ukey UNIQUE (payout_id, sequence_id) +); + +CREATE TABLE dw.payout_method +( + id bigserial NOT NULL, + version_id bigint NOT NULL, + payout_method_ref_id character varying NOT NULL, + name character varying NOT NULL, + description character varying NOT NULL, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + CONSTRAINT payout_method_pkey PRIMARY KEY (id) +); + +CREATE INDEX payout_method_idx ON dw.payout_method USING btree (payout_method_ref_id); +CREATE INDEX payout_method_version_id ON dw.payout_method USING btree (version_id); + + +CREATE TABLE dw.payout_tool +( + id bigserial NOT NULL, + cntrct_id bigint NOT NULL, + payout_tool_id character varying NOT NULL, + created_at timestamp without time zone NOT NULL, + currency_code character varying NOT NULL, + payout_tool_info dw.payout_tool_info NOT NULL, + payout_tool_info_russian_bank_account character varying, + payout_tool_info_russian_bank_name character varying, + payout_tool_info_russian_bank_post_account character varying, + payout_tool_info_russian_bank_bik character varying, + payout_tool_info_international_bank_account_holder character varying, + payout_tool_info_international_bank_name character varying, + payout_tool_info_international_bank_address character varying, + payout_tool_info_international_bank_iban character varying, + payout_tool_info_international_bank_bic character varying, + payout_tool_info_international_bank_local_code character varying, + payout_tool_info_international_bank_number character varying, + payout_tool_info_international_bank_aba_rtn character varying, + payout_tool_info_international_bank_country_code character varying, + payout_tool_info_international_correspondent_bank_account character varying, + payout_tool_info_international_correspondent_bank_name character varying, + payout_tool_info_international_correspondent_bank_address character varying, + payout_tool_info_international_correspondent_bank_bic character varying, + payout_tool_info_international_correspondent_bank_iban character varying, + payout_tool_info_international_correspondent_bank_number character varying, + payout_tool_info_international_correspondent_bank_aba_rtn character varying, + payout_tool_info_international_correspondent_bank_country_code character varying, + payout_tool_info_wallet_info_wallet_id character varying, + CONSTRAINT payout_tool_pkey PRIMARY KEY (id) +); + +CREATE INDEX payout_tool_idx ON dw.payout_tool USING btree (cntrct_id); + + +CREATE TABLE dw.provider +( + id bigserial NOT NULL, + version_id bigint NOT NULL, + provider_ref_id integer NOT NULL, + name character varying NOT NULL, + description character varying NOT NULL, + proxy_ref_id integer NOT NULL, + proxy_additional_json character varying NOT NULL, + terminal_json character varying, + abs_account character varying, + payment_terms_json character varying, + recurrent_paytool_terms_json character varying, + accounts_json character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + identity character varying, + wallet_terms_json character varying, + params_schema_json character varying, + CONSTRAINT provider_pkey PRIMARY KEY (id) +); + +CREATE INDEX provider_idx ON dw.provider USING btree (provider_ref_id); +CREATE INDEX provider_version_id ON dw.provider USING btree (version_id); + +CREATE TABLE dw.proxy +( + id bigserial NOT NULL, + version_id bigint NOT NULL, + proxy_ref_id integer NOT NULL, + name character varying NOT NULL, + description character varying NOT NULL, + url character varying NOT NULL, + options_json character varying NOT NULL, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + CONSTRAINT proxy_pkey PRIMARY KEY (id) +); + +CREATE INDEX proxy_idx ON dw.proxy USING btree (proxy_ref_id); +CREATE INDEX proxy_version_id ON dw.proxy USING btree (version_id); + + +CREATE TABLE dw.rate +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + source_id character varying NOT NULL, + lower_bound_inclusive timestamp without time zone NOT NULL, + upper_bound_exclusive timestamp without time zone NOT NULL, + source_symbolic_code character varying NOT NULL, + source_exponent smallint NOT NULL, + destination_symbolic_code character varying NOT NULL, + destination_exponent smallint NOT NULL, + exchange_rate_rational_p bigint NOT NULL, + exchange_rate_rational_q bigint NOT NULL, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + sequence_id bigint, + CONSTRAINT rate_pkey PRIMARY KEY (id) +); + +CREATE INDEX rate_event_created_at_idx ON dw.rate USING btree (event_created_at); +CREATE INDEX rate_source_id_idx ON dw.rate USING btree (source_id); +CREATE UNIQUE INDEX rate_ukey ON dw.rate USING btree (source_id, sequence_id, source_symbolic_code, + destination_symbolic_code); + + +CREATE TABLE dw.recurrent_payment_tool +( + id bigserial NOT NULL, + sequence_id integer NOT NULL, + change_id integer NOT NULL, + event_created_at timestamp without time zone NOT NULL, + recurrent_payment_tool_id character varying NOT NULL, + created_at timestamp without time zone NOT NULL, + party_id character varying NOT NULL, + shop_id character varying NOT NULL, + party_revision bigint, + domain_revision bigint NOT NULL, + status dw.recurrent_payment_tool_status NOT NULL, + status_failed_failure character varying, + payment_tool_type dw.payment_tool_type NOT NULL, + bank_card_token character varying, + bank_card_payment_system character varying, + bank_card_bin character varying, + bank_card_masked_pan character varying, + bank_card_token_provider character varying, + bank_card_issuer_country character varying, + bank_card_bank_name character varying, + bank_card_metadata_json character varying, + bank_card_is_cvv_empty boolean, + bank_card_exp_date_month integer, + bank_card_exp_date_year integer, + bank_card_cardholder_name character varying, + payment_terminal_type character varying, + digital_wallet_provider character varying, + digital_wallet_id character varying, + digital_wallet_token character varying, + crypto_currency character varying, + mobile_commerce_operator_legacy dw.mobile_operator_type, + mobile_commerce_phone_cc character varying, + mobile_commerce_phone_ctn character varying, + payment_session_id character varying, + client_info_ip_address character varying, + client_info_fingerprint character varying, + rec_token character varying, + route_provider_id integer, + route_terminal_id integer, + amount bigint, + currency_code character varying, + risk_score character varying, + session_payload_transaction_bound_trx_id character varying, + session_payload_transaction_bound_trx_extra_json character varying, + session_payload_transaction_bound_trx_additional_info_rrn character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + mobile_commerce_operator character varying, + CONSTRAINT recurrent_payment_tool_pkey PRIMARY KEY (id), + CONSTRAINT recurrent_payment_tool_uniq UNIQUE (recurrent_payment_tool_id, sequence_id, change_id) +); + +CREATE INDEX recurrent_payment_tool_id_idx ON dw.recurrent_payment_tool USING btree (recurrent_payment_tool_id); + +CREATE TABLE dw.refund +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + domain_revision bigint NOT NULL, + refund_id character varying NOT NULL, + payment_id character varying NOT NULL, + invoice_id character varying NOT NULL, + party_id character varying NOT NULL, + shop_id character varying NOT NULL, + created_at timestamp without time zone NOT NULL, + status dw.refund_status NOT NULL, + status_failed_failure character varying, + amount bigint, + currency_code character varying, + reason character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + session_payload_transaction_bound_trx_id character varying, + session_payload_transaction_bound_trx_extra_json character varying, + fee bigint, + provider_fee bigint, + external_fee bigint, + party_revision bigint, + sequence_id bigint, + change_id integer, + external_id character varying, + CONSTRAINT refund_pkey PRIMARY KEY (id), + CONSTRAINT refund_uniq UNIQUE (invoice_id, sequence_id, change_id) +); + +CREATE INDEX refund_created_at ON dw.refund USING btree (created_at); +CREATE INDEX refund_event_created_at ON dw.refund USING btree (event_created_at); +CREATE INDEX refund_external_id_idx ON dw.refund USING btree (external_id) WHERE (external_id IS NOT NULL); +CREATE INDEX refund_invoice_id ON dw.refund USING btree (invoice_id); +CREATE INDEX refund_party_id ON dw.refund USING btree (party_id); +CREATE INDEX refund_status ON dw.refund USING btree (status); + + +CREATE TABLE dw.shedlock +( + name character varying(64) NOT NULL, + lock_until timestamp(3) without time zone, + locked_at timestamp(3) without time zone, + locked_by character varying(255), + CONSTRAINT shedlock_pkey PRIMARY KEY (name) +); + +CREATE TABLE dw.shop +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + party_id character varying NOT NULL, + shop_id character varying NOT NULL, + created_at timestamp without time zone NOT NULL, + blocking dw.blocking NOT NULL, + blocking_unblocked_reason character varying, + blocking_unblocked_since timestamp without time zone, + blocking_blocked_reason character varying, + blocking_blocked_since timestamp without time zone, + suspension dw.suspension NOT NULL, + suspension_active_since timestamp without time zone, + suspension_suspended_since timestamp without time zone, + details_name character varying NOT NULL, + details_description character varying, + location_url character varying NOT NULL, + category_id integer NOT NULL, + account_currency_code character varying, + account_settlement bigint, + account_guarantee bigint, + account_payout bigint, + contract_id character varying NOT NULL, + payout_tool_id character varying, + payout_schedule_id integer, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + sequence_id integer, + change_id integer, + claim_effect_id integer, + CONSTRAINT shop_pkey PRIMARY KEY (id), + CONSTRAINT shop_uniq UNIQUE (party_id, shop_id, sequence_id, change_id, claim_effect_id) +); + +CREATE INDEX shop_created_at ON dw.shop USING btree (created_at); +CREATE INDEX shop_event_created_at ON dw.shop USING btree (event_created_at); +CREATE INDEX shop_party_id ON dw.shop USING btree (party_id); +CREATE INDEX shop_shop_id ON dw.shop USING btree (shop_id); + + +CREATE TABLE dw.shop_revision +( + id bigserial NOT NULL, + obj_id bigint NOT NULL, + revision bigint NOT NULL, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + CONSTRAINT shop_revision_pkey PRIMARY KEY (id) +); + +CREATE UNIQUE INDEX shop_revision_idx ON dw.shop_revision USING btree (obj_id, revision); + + +CREATE TABLE dw.source +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + event_occured_at timestamp without time zone NOT NULL, + sequence_id integer NOT NULL, + source_id character varying NOT NULL, + source_name character varying NOT NULL, + source_status dw.source_status NOT NULL, + resource_internal_details character varying, + account_id character varying, + identity_id character varying, + party_id character varying, + accounter_account_id bigint, + currency_code character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + external_id character varying, + CONSTRAINT source_pkey PRIMARY KEY (id), + CONSTRAINT source_uniq UNIQUE (source_id, sequence_id) +); + +CREATE INDEX source_event_created_at_idx ON dw.source USING btree (event_created_at); +CREATE INDEX source_event_occured_at_idx ON dw.source USING btree (event_occured_at); +CREATE INDEX source_id_idx ON dw.source USING btree (source_id); + + +CREATE TABLE dw.term_set_hierarchy +( + id bigserial NOT NULL, + version_id bigint NOT NULL, + term_set_hierarchy_ref_id integer NOT NULL, + name character varying, + description character varying, + parent_terms_ref_id integer, + term_sets_json character varying NOT NULL, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + CONSTRAINT term_set_hierarchy_pkey PRIMARY KEY (id) +); + +CREATE INDEX term_set_hierarchy_idx ON dw.term_set_hierarchy USING btree (term_set_hierarchy_ref_id); +CREATE INDEX term_set_hierarchy_version_id ON dw.term_set_hierarchy USING btree (version_id); + + +CREATE TABLE dw.terminal +( + id bigserial NOT NULL, + version_id bigint NOT NULL, + terminal_ref_id integer NOT NULL, + name character varying NOT NULL, + description character varying NOT NULL, + options_json character varying, + risk_coverage character varying, + terms_json character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + external_terminal_id character varying, + external_merchant_id character varying, + mcc character varying, + terminal_provider_ref_id integer, + CONSTRAINT terminal_pkey PRIMARY KEY (id) +); + +CREATE INDEX terminal_idx ON dw.terminal USING btree (terminal_ref_id); +CREATE INDEX terminal_version_id ON dw.terminal USING btree (version_id); + + +CREATE TABLE dw.trade_bloc +( + id bigserial NOT NULL, + version_id bigint NOT NULL, + trade_bloc_ref_id character varying NOT NULL, + name character varying NOT NULL, + description character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + CONSTRAINT trade_bloc_pkey PRIMARY KEY (id) +); + +CREATE TABLE dw.wallet +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + event_occured_at timestamp without time zone NOT NULL, + sequence_id integer NOT NULL, + wallet_id character varying NOT NULL, + wallet_name character varying NOT NULL, + identity_id character varying, + party_id character varying, + currency_code character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + account_id character varying, + accounter_account_id bigint, + external_id character varying, + CONSTRAINT wallet_pkey PRIMARY KEY (id), + CONSTRAINT wallet_uniq UNIQUE (wallet_id, sequence_id) +); + +CREATE INDEX wallet_event_created_at_idx ON dw.wallet USING btree (event_created_at); +CREATE INDEX wallet_event_occured_at_idx ON dw.wallet USING btree (event_occured_at); +CREATE INDEX wallet_id_idx ON dw.wallet USING btree (wallet_id); + + +CREATE TABLE dw.withdrawal +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + event_occured_at timestamp without time zone NOT NULL, + sequence_id integer NOT NULL, + wallet_id character varying NOT NULL, + destination_id character varying NOT NULL, + withdrawal_id character varying NOT NULL, + provider_id_legacy character varying, + amount bigint NOT NULL, + currency_code character varying NOT NULL, + withdrawal_status dw.withdrawal_status NOT NULL, + withdrawal_transfer_status dw.withdrawal_transfer_status, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + fee bigint, + provider_fee bigint, + external_id character varying, + context_json character varying, + withdrawal_status_failed_failure_json character varying, + provider_id integer, + CONSTRAINT withdrawal_pkey PRIMARY KEY (id), + CONSTRAINT withdrawal_uniq UNIQUE (withdrawal_id, sequence_id) +); + +CREATE INDEX withdrawal_event_created_at_idx ON dw.withdrawal USING btree (event_created_at); +CREATE INDEX withdrawal_event_occured_at_idx ON dw.withdrawal USING btree (event_occured_at); +CREATE INDEX withdrawal_id_idx ON dw.withdrawal USING btree (withdrawal_id); + + +CREATE TABLE dw.withdrawal_provider +( + id bigserial NOT NULL, + version_id bigint NOT NULL, + withdrawal_provider_ref_id integer NOT NULL, + name character varying NOT NULL, + description character varying, + proxy_ref_id integer NOT NULL, + proxy_additional_json character varying NOT NULL, + identity character varying, + withdrawal_terms_json character varying, + accounts_json character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + CONSTRAINT withdrawal_provider_pkey PRIMARY KEY (id) +); + +CREATE INDEX withdrawal_provider_idx ON dw.withdrawal_provider USING btree (withdrawal_provider_ref_id); +CREATE INDEX withdrawal_provider_version_id ON dw.withdrawal_provider USING btree (version_id); + + +CREATE TABLE dw.withdrawal_session +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + event_occured_at timestamp without time zone NOT NULL, + sequence_id integer NOT NULL, + withdrawal_session_id character varying NOT NULL, + withdrawal_session_status dw.withdrawal_session_status NOT NULL, + provider_id_legacy character varying, + withdrawal_id character varying NOT NULL, + destination_card_token character varying, + destination_card_payment_system dw.bank_card_payment_system, + destination_card_bin character varying, + destination_card_masked_pan character varying, + amount bigint NOT NULL, + currency_code character varying NOT NULL, + sender_party_id character varying, + sender_provider_id character varying, + sender_class_id character varying, + sender_contract_id character varying, + receiver_party_id character varying, + receiver_provider_id character varying, + receiver_class_id character varying, + receiver_contract_id character varying, + adapter_state character varying, + tran_info_id character varying, + tran_info_timestamp timestamp without time zone, + tran_info_json character varying, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + current boolean DEFAULT true NOT NULL, + failure_json character varying, + resource_type dw.destination_resource_type NOT NULL, + resource_crypto_wallet_id character varying, + resource_crypto_wallet_type character varying, + resource_crypto_wallet_data character varying, + resource_bank_card_type character varying, + resource_bank_card_issuer_country character varying, + resource_bank_card_bank_name character varying, + tran_additional_info character varying, + tran_additional_info_rrn character varying, + tran_additional_info_json character varying, + provider_id integer, + resource_digital_wallet_id character varying, + resource_digital_wallet_data character varying, + CONSTRAINT withdrawal_session_pk PRIMARY KEY (id), + CONSTRAINT withdrawal_session_uniq UNIQUE (withdrawal_session_id, sequence_id) +); + +CREATE INDEX withdrawal_session_event_created_at_idx ON dw.withdrawal_session USING btree (event_created_at); +CREATE INDEX withdrawal_session_event_occured_at_idx ON dw.withdrawal_session USING btree (event_occured_at); +CREATE INDEX withdrawal_session_id_idx ON dw.withdrawal_session USING btree (withdrawal_session_id); \ No newline at end of file diff --git a/src/main/resources/db/migration/V20__1.0.28_fix_withdrawal_session.sql b/src/main/resources/db/migration/V20__1.0.28_fix_withdrawal_session.sql deleted file mode 100644 index 4dcdc01f..00000000 --- a/src/main/resources/db/migration/V20__1.0.28_fix_withdrawal_session.sql +++ /dev/null @@ -1,7 +0,0 @@ - - ALTER TABLE nw.withdrawal_session ALTER COLUMN sender_party_id DROP NOT NULL; - ALTER TABLE nw.withdrawal_session ALTER COLUMN sender_provider_id DROP NOT NULL; - ALTER TABLE nw.withdrawal_session ALTER COLUMN sender_class_id DROP NOT NULL; - ALTER TABLE nw.withdrawal_session ALTER COLUMN receiver_party_id DROP NOT NULL; - ALTER TABLE nw.withdrawal_session ALTER COLUMN receiver_provider_id DROP NOT NULL; - ALTER TABLE nw.withdrawal_session ALTER COLUMN receiver_class_id DROP NOT NULL; \ No newline at end of file diff --git a/src/main/resources/db/migration/V21__1.0.33_remove_unused_columns_in_payout.sql b/src/main/resources/db/migration/V21__1.0.33_remove_unused_columns_in_payout.sql deleted file mode 100644 index 904621e0..00000000 --- a/src/main/resources/db/migration/V21__1.0.33_remove_unused_columns_in_payout.sql +++ /dev/null @@ -1,2 +0,0 @@ -alter table nw.payout drop column initiator_id; -alter table nw.payout drop column initiator_type; \ No newline at end of file diff --git a/src/main/resources/db/migration/V22__1.0.35_remove_revision_not_null_constraint.sql b/src/main/resources/db/migration/V22__1.0.35_remove_revision_not_null_constraint.sql deleted file mode 100644 index 9947bb5f..00000000 --- a/src/main/resources/db/migration/V22__1.0.35_remove_revision_not_null_constraint.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE nw.contract ALTER COLUMN revision DROP NOT NULL; -ALTER TABLE nw.contractor ALTER COLUMN revision DROP NOT NULL; -ALTER TABLE nw.shop ALTER COLUMN revision DROP NOT NULL; \ No newline at end of file diff --git a/src/main/resources/db/migration/V23__1.0.36_add_wallet_payout_tool_info_type.sql b/src/main/resources/db/migration/V23__1.0.36_add_wallet_payout_tool_info_type.sql deleted file mode 100644 index 711a0c23..00000000 --- a/src/main/resources/db/migration/V23__1.0.36_add_wallet_payout_tool_info_type.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TYPE nw.payout_tool_info ADD VALUE 'wallet_info'; \ No newline at end of file diff --git a/src/main/resources/db/migration/V24__1.0.36_add_wallet_payout_tool_info_columns.sql b/src/main/resources/db/migration/V24__1.0.36_add_wallet_payout_tool_info_columns.sql deleted file mode 100644 index 310d09a1..00000000 --- a/src/main/resources/db/migration/V24__1.0.36_add_wallet_payout_tool_info_columns.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE nw.payout_tool ADD COLUMN payout_tool_info_wallet_info_wallet_id CHARACTER VARYING; \ No newline at end of file diff --git a/src/main/resources/db/migration/V25__1.0.37_add_payout_type.sql b/src/main/resources/db/migration/V25__1.0.37_add_payout_type.sql deleted file mode 100644 index 2ddcb19c..00000000 --- a/src/main/resources/db/migration/V25__1.0.37_add_payout_type.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TYPE nw.payout_type ADD VALUE 'wallet'; \ No newline at end of file diff --git a/src/main/resources/db/migration/V26__1.0.39_actualized_fistful_proto.sql b/src/main/resources/db/migration/V26__1.0.39_actualized_fistful_proto.sql deleted file mode 100644 index 9e9072ce..00000000 --- a/src/main/resources/db/migration/V26__1.0.39_actualized_fistful_proto.sql +++ /dev/null @@ -1,16 +0,0 @@ -ALTER TABLE nw.deposit ADD COLUMN external_id CHARACTER VARYING; -ALTER TABLE nw.destination ADD COLUMN external_id CHARACTER VARYING; -ALTER TABLE nw.identity ADD COLUMN external_id CHARACTER VARYING; -ALTER TABLE nw.source ADD COLUMN external_id CHARACTER VARYING; -ALTER TABLE nw.wallet ADD COLUMN external_id CHARACTER VARYING; -ALTER TABLE nw.withdrawal ADD COLUMN external_id CHARACTER VARYING; - -ALTER TABLE nw.identity ADD COLUMN blocked BOOLEAN; -ALTER TABLE nw.identity ADD COLUMN context_json CHARACTER VARYING; -ALTER TABLE nw.challenge ADD COLUMN proofs_json CHARACTER VARYING; - -ALTER TABLE nw.destination ADD COLUMN created_at TIMESTAMP WITHOUT TIME ZONE; -ALTER TABLE nw.destination ADD COLUMN context_json CHARACTER VARYING; -ALTER TABLE nw.withdrawal ADD COLUMN context_json CHARACTER VARYING; - -ALTER TABLE nw.withdrawal_session ADD COLUMN failure_json CHARACTER VARYING; diff --git a/src/main/resources/db/migration/V27__1.0.43_add_payment_method_type.sql b/src/main/resources/db/migration/V27__1.0.43_add_payment_method_type.sql deleted file mode 100644 index 96aed879..00000000 --- a/src/main/resources/db/migration/V27__1.0.43_add_payment_method_type.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TYPE nw.payment_method_type ADD VALUE 'empty_cvv_bank_card'; \ No newline at end of file diff --git a/src/main/resources/db/migration/V28__1.0.44_add_change_id_remove_event_id.sql b/src/main/resources/db/migration/V28__1.0.44_add_change_id_remove_event_id.sql deleted file mode 100644 index 4f0c7d7f..00000000 --- a/src/main/resources/db/migration/V28__1.0.44_add_change_id_remove_event_id.sql +++ /dev/null @@ -1,19 +0,0 @@ -ALTER TABLE nw.invoice ADD COLUMN sequence_id BIGINT; -ALTER TABLE nw.invoice ADD COLUMN change_id INT; -ALTER TABLE nw.invoice DROP COLUMN event_id; -ALTER TABLE nw.invoice ADD CONSTRAINT invoice_uniq UNIQUE (invoice_id, sequence_id, change_id); - -ALTER TABLE nw.payment ADD COLUMN sequence_id BIGINT; -ALTER TABLE nw.payment ADD COLUMN change_id INT; -ALTER TABLE nw.payment DROP COLUMN event_id; -ALTER TABLE nw.payment ADD CONSTRAINT payment_uniq UNIQUE (invoice_id, sequence_id, change_id); - -ALTER TABLE nw.refund ADD COLUMN sequence_id BIGINT; -ALTER TABLE nw.refund ADD COLUMN change_id INT; -ALTER TABLE nw.refund DROP COLUMN event_id; -ALTER TABLE nw.refund ADD CONSTRAINT refund_uniq UNIQUE (invoice_id, sequence_id, change_id); - -ALTER TABLE nw.adjustment ADD COLUMN sequence_id BIGINT; -ALTER TABLE nw.adjustment ADD COLUMN change_id INT; -ALTER TABLE nw.adjustment DROP COLUMN event_id; -ALTER TABLE nw.adjustment ADD CONSTRAINT adjustment_uniq UNIQUE (invoice_id, sequence_id, change_id); diff --git a/src/main/resources/db/migration/V29__1.0.46_add_additional_payment_info.sql b/src/main/resources/db/migration/V29__1.0.46_add_additional_payment_info.sql deleted file mode 100644 index 2a2e2371..00000000 --- a/src/main/resources/db/migration/V29__1.0.46_add_additional_payment_info.sql +++ /dev/null @@ -1,12 +0,0 @@ -ALTER TABLE nw.payment ADD COLUMN trx_additional_info_rrn CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN trx_additional_info_approval_code CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN trx_additional_info_acs_url CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN trx_additional_info_pareq CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN trx_additional_info_md CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN trx_additional_info_term_url CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN trx_additional_info_pares CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN trx_additional_info_eci CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN trx_additional_info_cavv CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN trx_additional_info_xid CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN trx_additional_info_cavv_algorithm CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN trx_additional_info_three_ds_verification CHARACTER VARYING; diff --git a/src/main/resources/db/migration/V2__cash_flow_aggregate_functions.sql b/src/main/resources/db/migration/V2__cash_flow_aggregate_functions.sql deleted file mode 100644 index b7b2969c..00000000 --- a/src/main/resources/db/migration/V2__cash_flow_aggregate_functions.sql +++ /dev/null @@ -1,436 +0,0 @@ -create function nw.get_cashflow_sum(_cash_flow nw.cash_flow, obj_type nw.payment_change_type, source_account_type nw.cash_flow_account, source_account_type_values varchar[], destination_account_type nw.cash_flow_account, destination_account_type_values varchar[]) -returns bigint -language plpgsql -immutable -as $$ -begin - return ( - coalesce( - ( - select sum(amount) from (select ($1).*) as cash_flow - where cash_flow.obj_type = $2 - and cash_flow.source_account_type = $3 - and cash_flow.source_account_type_value = ANY ($4) - and cash_flow.destination_account_type = $5 - and cash_flow.destination_account_type_value = ANY ($6) - and ( - (cash_flow.obj_type = 'adjustment' and cash_flow.adj_flow_type = 'new_cash_flow') - or (cash_flow.obj_type != 'adjustment' and cash_flow.adj_flow_type is null) - ) - ), 0) - ); -end; -$$; - -create function nw.cashflow_sum_finalfunc(amounts bigint[]) -returns bigint -immutable -strict -language plpgsql -as $$ -begin - return (select sum(amount_values) from unnest($1) as amount_values); -end; -$$; - -create function nw.get_payment_amount_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'payment'::nw.payment_change_type, - 'provider'::nw.cash_flow_account, - '{"settlement"}', - 'merchant'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -create aggregate nw.get_payment_amount(nw.cash_flow) -( - sfunc = nw.get_payment_amount_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - -create function nw.get_payment_fee_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'payment'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"settlement"}', - 'system'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -create aggregate nw.get_payment_fee(nw.cash_flow) -( - sfunc = nw.get_payment_fee_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - -create function nw.get_payment_external_fee_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'payment'::nw.payment_change_type, - 'system'::nw.cash_flow_account, - '{"settlement"}', - 'external'::nw.cash_flow_account, - '{"income", "outcome"}' - ) - ); -end; -$$; - -create aggregate nw.get_payment_external_fee(nw.cash_flow) -( - sfunc = nw.get_payment_external_fee_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - -create function nw.get_payment_provider_fee_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'payment'::nw.payment_change_type, - 'system'::nw.cash_flow_account, - '{"settlement"}', - 'provider'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -create aggregate nw.get_payment_provider_fee(nw.cash_flow) -( - sfunc = nw.get_payment_provider_fee_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - -create function nw.get_payment_guarantee_deposit_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'payment'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"settlement"}', - 'merchant'::nw.cash_flow_account, - '{"guarantee"}' - ) - ); -end; -$$; - -create aggregate nw.get_payment_guarantee_deposit(nw.cash_flow) -( - sfunc = nw.get_payment_guarantee_deposit_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - -create function nw.get_refund_amount_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'refund'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"settlement"}', - 'provider'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -create aggregate nw.get_refund_amount(nw.cash_flow) -( - sfunc = nw.get_refund_amount_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - -create function nw.get_refund_fee_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'refund'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"settlement"}', - 'system'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -create aggregate nw.get_refund_fee(nw.cash_flow) -( - sfunc = nw.get_refund_fee_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - -create function nw.get_refund_external_fee_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'refund'::nw.payment_change_type, - 'system'::nw.cash_flow_account, - '{"settlement"}', - 'external'::nw.cash_flow_account, - '{"income", "outcome"}' - ) - ); -end; -$$; - -create aggregate nw.get_refund_external_fee(nw.cash_flow) -( - sfunc = nw.get_refund_external_fee_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - -create function nw.get_refund_provider_fee_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'refund'::nw.payment_change_type, - 'system'::nw.cash_flow_account, - '{"settlement"}', - 'provider'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -create aggregate nw.get_refund_provider_fee(nw.cash_flow) -( - sfunc = nw.get_refund_provider_fee_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - -create function nw.get_payout_amount_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'payout'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"settlement"}', - 'merchant'::nw.cash_flow_account, - '{"payout"}' - ) - ); -end; -$$; - -create aggregate nw.get_payout_amount(nw.cash_flow) -( - sfunc = nw.get_payout_amount_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - -create function nw.get_payout_fee_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'payout'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"settlement"}', - 'system'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -create aggregate nw.get_payout_fee(nw.cash_flow) -( - sfunc = nw.get_payout_fee_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - - -create function nw.get_payout_fixed_fee_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'payout'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"payout"}', - 'system'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -create aggregate nw.get_payout_fixed_fee(nw.cash_flow) -( - sfunc = nw.get_payout_fixed_fee_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - -create function nw.get_adjustment_amount_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'adjustment'::nw.payment_change_type, - 'provider'::nw.cash_flow_account, - '{"settlement"}', - 'merchant'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -create aggregate nw.get_adjustment_amount(nw.cash_flow) -( - sfunc = nw.get_adjustment_amount_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - -create function nw.get_adjustment_fee_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'adjustment'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"settlement"}', - 'system'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -create aggregate nw.get_adjustment_fee(nw.cash_flow) -( - sfunc = nw.get_adjustment_fee_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - -create function nw.get_adjustment_external_fee_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'adjustment'::nw.payment_change_type, - 'system'::nw.cash_flow_account, - '{"settlement"}', - 'external'::nw.cash_flow_account, - '{"income", "outcome"}' - ) - ); -end; -$$; - -create aggregate nw.get_adjustment_external_fee(nw.cash_flow) -( - sfunc = nw.get_adjustment_external_fee_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - -create function nw.get_adjustment_provider_fee_sfunc(amounts bigint[], cash_flow nw.cash_flow) -returns bigint[] -language plpgsql -as $$ -begin - return $1 || ( - nw.get_cashflow_sum( - $2, - 'adjustment'::nw.payment_change_type, - 'system'::nw.cash_flow_account, - '{"settlement"}', - 'provider'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -create aggregate nw.get_adjustment_provider_fee(nw.cash_flow) -( - sfunc = nw.get_adjustment_provider_fee_sfunc, - stype = bigint[], - finalfunc = cashflow_sum_finalfunc -); - diff --git a/src/main/resources/db/migration/V30__1.0.46_crypto_payment_tool_type.sql b/src/main/resources/db/migration/V30__1.0.46_crypto_payment_tool_type.sql deleted file mode 100644 index 346c33fb..00000000 --- a/src/main/resources/db/migration/V30__1.0.46_crypto_payment_tool_type.sql +++ /dev/null @@ -1 +0,0 @@ -alter type nw.payment_tool_type add value 'crypto_currency'; \ No newline at end of file diff --git a/src/main/resources/db/migration/V31__1.0.46_crypto_wallet.sql b/src/main/resources/db/migration/V31__1.0.46_crypto_wallet.sql deleted file mode 100644 index 093e6b0c..00000000 --- a/src/main/resources/db/migration/V31__1.0.46_crypto_wallet.sql +++ /dev/null @@ -1,7 +0,0 @@ -alter table nw.destination add column resource_crypto_wallet_id character varying; -alter table nw.destination add column resource_crypto_wallet_type character varying; - -alter table nw.payment add column payer_crypto_currency_type character varying; -create type nw.destination_resource_type as enum ('bank_card', 'crypto_wallet'); -alter table nw.destination add column resource_type nw.destination_resource_type; -update nw.destination set resource_type = 'bank_card'::nw.destination_resource_type; \ No newline at end of file diff --git a/src/main/resources/db/migration/V32__add_resource_type_in_withdrawal_session.sql b/src/main/resources/db/migration/V32__add_resource_type_in_withdrawal_session.sql deleted file mode 100644 index 15b1feb5..00000000 --- a/src/main/resources/db/migration/V32__add_resource_type_in_withdrawal_session.sql +++ /dev/null @@ -1,8 +0,0 @@ -alter table nw.destination alter column resource_bank_card_token drop not null; -alter table nw.destination alter column resource_type set not null; -alter table nw.withdrawal_session alter column destination_card_token drop not null; -alter table nw.withdrawal_session add column resource_type nw.destination_resource_type; -update nw.withdrawal_session set resource_type = 'bank_card'::nw.destination_resource_type; -alter table nw.withdrawal_session alter column resource_type set not null; -alter table nw.withdrawal_session add column resource_crypto_wallet_id character varying; -alter table nw.withdrawal_session add column resource_crypto_wallet_type character varying; \ No newline at end of file diff --git a/src/main/resources/db/migration/V33__1.0.51_add_adjustment_status.sql b/src/main/resources/db/migration/V33__1.0.51_add_adjustment_status.sql deleted file mode 100644 index 739b69bb..00000000 --- a/src/main/resources/db/migration/V33__1.0.51_add_adjustment_status.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TYPE nw.adjustment_status ADD VALUE 'processed'; \ No newline at end of file diff --git a/src/main/resources/db/migration/V34__1.0.52_update_fistful_proto.sql b/src/main/resources/db/migration/V34__1.0.52_update_fistful_proto.sql deleted file mode 100644 index 4abb955b..00000000 --- a/src/main/resources/db/migration/V34__1.0.52_update_fistful_proto.sql +++ /dev/null @@ -1,16 +0,0 @@ -alter table nw.destination add column resource_crypto_wallet_data character varying; -alter table nw.destination add column resource_bank_card_type character varying; -alter table nw.destination add column resource_bank_card_issuer_country character varying; -alter table nw.destination add column resource_bank_card_bank_name character varying; - -alter table nw.withdrawal_session add column resource_crypto_wallet_data character varying; -alter table nw.withdrawal_session add column resource_bank_card_type character varying; -alter table nw.withdrawal_session add column resource_bank_card_issuer_country character varying; -alter table nw.withdrawal_session add column resource_bank_card_bank_name character varying; -alter table nw.withdrawal_session add column tran_additional_info character varying; -alter table nw.withdrawal_session add column tran_additional_info_rrn character varying; -alter table nw.withdrawal_session add column tran_additional_info_json character varying; - -alter table nw.withdrawal add column withdrawal_status_failed_failure_json character varying; - -alter table nw.withdrawal_session drop column destination_name; diff --git a/src/main/resources/db/migration/V35__1.0.53_xrates_sequence_id.sql b/src/main/resources/db/migration/V35__1.0.53_xrates_sequence_id.sql deleted file mode 100644 index 9b441f9c..00000000 --- a/src/main/resources/db/migration/V35__1.0.53_xrates_sequence_id.sql +++ /dev/null @@ -1,6 +0,0 @@ -alter table nw.rate add column sequence_id bigint; -alter table nw.rate add column change_id int; - -CREATE UNIQUE INDEX idx_uniq ON nw.rate(source_id, sequence_id, change_id, source_symbolic_code, destination_symbolic_code); - -delete from nw.rate where event_id > 1339; \ No newline at end of file diff --git a/src/main/resources/db/migration/V36__add_capture_started_reason.sql b/src/main/resources/db/migration/V36__add_capture_started_reason.sql deleted file mode 100644 index 0436b636..00000000 --- a/src/main/resources/db/migration/V36__add_capture_started_reason.sql +++ /dev/null @@ -1 +0,0 @@ -alter table nw.payment add status_captured_started_reason character varying; diff --git a/src/main/resources/db/migration/V37__add_mobile_commerce_type.sql b/src/main/resources/db/migration/V37__add_mobile_commerce_type.sql deleted file mode 100644 index ce3eed6f..00000000 --- a/src/main/resources/db/migration/V37__add_mobile_commerce_type.sql +++ /dev/null @@ -1 +0,0 @@ -alter type nw.payment_tool_type add value 'mobile_commerce'; diff --git a/src/main/resources/db/migration/V38__add_payment_mobile_commerce.sql b/src/main/resources/db/migration/V38__add_payment_mobile_commerce.sql deleted file mode 100644 index fdd9f494..00000000 --- a/src/main/resources/db/migration/V38__add_payment_mobile_commerce.sql +++ /dev/null @@ -1,7 +0,0 @@ -create type mobile_operator_type as enum ('mts', 'beeline', 'megafone', 'tele2', 'yota'); - -alter table nw.payment add column payer_mobile_operator nw.mobile_operator_type; - -alter table nw.payment add column payer_mobile_phone_cc character varying; - -alter table nw.payment add column payer_mobile_phone_ctn character varying; diff --git a/src/main/resources/db/migration/V39__add_payment_method_mobile.sql b/src/main/resources/db/migration/V39__add_payment_method_mobile.sql deleted file mode 100644 index 4f4d294e..00000000 --- a/src/main/resources/db/migration/V39__add_payment_method_mobile.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TYPE nw.payment_method_type ADD VALUE 'crypto_currency'; -ALTER TYPE nw.payment_method_type ADD VALUE 'mobile'; diff --git a/src/main/resources/db/migration/V3__party_revision.sql b/src/main/resources/db/migration/V3__party_revision.sql deleted file mode 100644 index 7e4a3061..00000000 --- a/src/main/resources/db/migration/V3__party_revision.sql +++ /dev/null @@ -1,5 +0,0 @@ -TRUNCATE nw.party, nw.contract, nw.contractor, nw.shop CASCADE; - -ALTER TABLE nw.contract ADD COLUMN revision BIGINT NOT NULL; -ALTER TABLE nw.contractor ADD COLUMN revision BIGINT NOT NULL; -ALTER TABLE nw.shop ADD COLUMN revision BIGINT NOT NULL; \ No newline at end of file diff --git a/src/main/resources/db/migration/V40__1.0.56_batch.sql b/src/main/resources/db/migration/V40__1.0.56_batch.sql deleted file mode 100644 index 18d4d333..00000000 --- a/src/main/resources/db/migration/V40__1.0.56_batch.sql +++ /dev/null @@ -1,14 +0,0 @@ -CREATE SEQUENCE nw.inv_seq - INCREMENT 1 - START 200000000 - MINVALUE 200000000 - CACHE 1; - -CREATE SEQUENCE nw.pmnt_seq - INCREMENT 1 - START 600000000 - MINVALUE 600000000 - CACHE 1; - -alter table nw.payment add column capture_started_params_cart_json character varying; -alter table nw.invoice_cart drop constraint if exists fk_cart_to_invoice; \ No newline at end of file diff --git a/src/main/resources/db/migration/V41__1.0.58_recurrent_payment_tool.sql b/src/main/resources/db/migration/V41__1.0.58_recurrent_payment_tool.sql deleted file mode 100644 index 348a14ff..00000000 --- a/src/main/resources/db/migration/V41__1.0.58_recurrent_payment_tool.sql +++ /dev/null @@ -1,57 +0,0 @@ -CREATE TYPE nw.recurrent_payment_tool_status AS ENUM('created', 'acquired', 'abandoned', 'failed'); - -CREATE TABLE nw.recurrent_payment_tool( - id BIGSERIAL NOT NULL, - event_id BIGINT NOT NULL, - sequence_id INT NOT NULL, - change_id INT NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - recurrent_payment_tool_id CHARACTER VARYING NOT NULL, - created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - party_id CHARACTER VARYING NOT NULL, - shop_id CHARACTER VARYING NOT NULL, - party_revision BIGINT, - domain_revision BIGINT NOT NULL, - status nw.recurrent_payment_tool_status NOT NULL, - status_failed_failure CHARACTER VARYING, - payment_tool_type nw.payment_tool_type NOT NULL, - bank_card_token CHARACTER VARYING, - bank_card_payment_system CHARACTER VARYING, - bank_card_bin CHARACTER VARYING, - bank_card_masked_pan CHARACTER VARYING, - bank_card_token_provider CHARACTER VARYING, - bank_card_issuer_country CHARACTER VARYING, - bank_card_bank_name CHARACTER VARYING, - bank_card_metadata_json CHARACTER VARYING, - bank_card_is_cvv_empty BOOLEAN, - bank_card_exp_date_month INT, - bank_card_exp_date_year INT, - bank_card_cardholder_name CHARACTER VARYING, - payment_terminal_type CHARACTER VARYING, - digital_wallet_provider CHARACTER VARYING, - digital_wallet_id CHARACTER VARYING, - digital_wallet_token CHARACTER VARYING, - crypto_currency CHARACTER VARYING, - mobile_commerce_operator nw.mobile_operator_type, - mobile_commerce_phone_cc CHARACTER VARYING, - mobile_commerce_phone_ctn CHARACTER VARYING, - payment_session_id CHARACTER VARYING, - client_info_ip_address CHARACTER VARYING, - client_info_fingerprint CHARACTER VARYING, - rec_token CHARACTER VARYING, - route_provider_id INT, - route_terminal_id INT, - amount BIGINT NOT NULL, - currency_code CHARACTER VARYING NOT NULL, - risk_score CHARACTER VARYING, - session_payload_transaction_bound_trx_id CHARACTER VARYING, - session_payload_transaction_bound_trx_extra_json CHARACTER VARYING, - session_payload_transaction_bound_trx_additional_info_rrn CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT recurrent_payment_tool_pkey PRIMARY KEY (id) -); - -CREATE INDEX recurrent_payment_tool_event_id_idx on nw.recurrent_payment_tool(event_id); -CREATE INDEX recurrent_payment_tool_id_idx on nw.recurrent_payment_tool(recurrent_payment_tool_id); -ALTER TABLE nw.recurrent_payment_tool ADD CONSTRAINT recurrent_payment_tool_uniq UNIQUE (recurrent_payment_tool_id, sequence_id, change_id); \ No newline at end of file diff --git a/src/main/resources/db/migration/V42__1.0.60_external_id.sql b/src/main/resources/db/migration/V42__1.0.60_external_id.sql deleted file mode 100644 index dcc3729a..00000000 --- a/src/main/resources/db/migration/V42__1.0.60_external_id.sql +++ /dev/null @@ -1,8 +0,0 @@ -alter table nw.invoice add column if not exists external_id character varying; -create index if not exists invoice_external_id_idx on nw.invoice(external_id) where external_id is not null; - -alter table nw.payment add column if not exists external_id character varying; -create index if not exists payment_external_id_idx on nw.payment(external_id) where external_id is not null; - -alter table nw.refund add column if not exists external_id character varying; -create index if not exists refund_external_id_idx on nw.refund(external_id) where external_id is not null; diff --git a/src/main/resources/db/migration/V43__1.0.61_drop_not_null.sql b/src/main/resources/db/migration/V43__1.0.61_drop_not_null.sql deleted file mode 100644 index 70293c14..00000000 --- a/src/main/resources/db/migration/V43__1.0.61_drop_not_null.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE nw.recurrent_payment_tool ALTER COLUMN amount DROP NOT NULL; -ALTER TABLE nw.recurrent_payment_tool ALTER COLUMN currency_code DROP NOT NULL; \ No newline at end of file diff --git a/src/main/resources/db/migration/V44__1.0.62_add_payment_bank_info_columns.sql b/src/main/resources/db/migration/V44__1.0.62_add_payment_bank_info_columns.sql deleted file mode 100644 index b80c1553..00000000 --- a/src/main/resources/db/migration/V44__1.0.62_add_payment_bank_info_columns.sql +++ /dev/null @@ -1,2 +0,0 @@ -alter table nw.payment add column if not exists payer_issuer_country character varying; -alter table nw.payment add column if not exists payer_bank_name character varying; diff --git a/src/main/resources/db/migration/V45__1.0.62_add_payment_system_in_rate.sql b/src/main/resources/db/migration/V45__1.0.62_add_payment_system_in_rate.sql deleted file mode 100644 index 2495258b..00000000 --- a/src/main/resources/db/migration/V45__1.0.62_add_payment_system_in_rate.sql +++ /dev/null @@ -1,4 +0,0 @@ -alter table nw.rate add column payment_system character varying not null default ''; - -drop index idx_uniq; -create unique index rate_ukey on nw.rate(source_id, sequence_id, change_id, source_symbolic_code, destination_symbolic_code, payment_system); \ No newline at end of file diff --git a/src/main/resources/db/migration/V46__1.0.64_withdrawal_provider.sql b/src/main/resources/db/migration/V46__1.0.64_withdrawal_provider.sql deleted file mode 100644 index 9143d5bc..00000000 --- a/src/main/resources/db/migration/V46__1.0.64_withdrawal_provider.sql +++ /dev/null @@ -1,19 +0,0 @@ ---provider-- -CREATE TABLE nw.withdrawal_provider( - id BIGSERIAL NOT NULL, - version_id BIGINT NOT NULL, - withdrawal_provider_ref_id INT NOT NULL, - name CHARACTER VARYING NOT NULL, - description CHARACTER VARYING, - proxy_ref_id INT NOT NULL, - proxy_additional_json CHARACTER VARYING NOT NULL, - identity CHARACTER VARYING, - withdrawal_terms_json CHARACTER VARYING, - accounts_json CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT withdrawal_provider_pkey PRIMARY KEY (id) -); - -CREATE INDEX withdrawal_provider_version_id on nw.withdrawal_provider(version_id); -CREATE INDEX withdrawal_provider_idx on nw.withdrawal_provider(withdrawal_provider_ref_id); \ No newline at end of file diff --git a/src/main/resources/db/migration/V47__1.0.65_add_payment_status_adjustment.sql b/src/main/resources/db/migration/V47__1.0.65_add_payment_status_adjustment.sql deleted file mode 100644 index 48833c56..00000000 --- a/src/main/resources/db/migration/V47__1.0.65_add_payment_status_adjustment.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE nw.adjustment ADD COLUMN IF NOT EXISTS payment_status nw.payment_status; diff --git a/src/main/resources/db/migration/V48__1.0.66_add_sequence_change_id.sql b/src/main/resources/db/migration/V48__1.0.66_add_sequence_change_id.sql deleted file mode 100644 index 9e341a8a..00000000 --- a/src/main/resources/db/migration/V48__1.0.66_add_sequence_change_id.sql +++ /dev/null @@ -1,18 +0,0 @@ -ALTER TABLE nw.party ADD COLUMN sequence_id INT; -ALTER TABLE nw.party ADD COLUMN change_id INT; -ALTER TABLE nw.party ADD CONSTRAINT party_uniq UNIQUE (party_id, sequence_id, change_id); - -ALTER TABLE nw.shop ADD COLUMN sequence_id INT; -ALTER TABLE nw.shop ADD COLUMN change_id INT; -ALTER TABLE nw.shop ADD COLUMN claim_effect_id INT; -ALTER TABLE nw.shop ADD CONSTRAINT shop_uniq UNIQUE (party_id, sequence_id, change_id, claim_effect_id); - -ALTER TABLE nw.contract ADD COLUMN sequence_id INT; -ALTER TABLE nw.contract ADD COLUMN change_id INT; -ALTER TABLE nw.contract ADD COLUMN claim_effect_id INT; -ALTER TABLE nw.contract ADD CONSTRAINT contract_uniq UNIQUE (party_id, sequence_id, change_id, claim_effect_id); - -ALTER TABLE nw.contractor ADD COLUMN sequence_id INT; -ALTER TABLE nw.contractor ADD COLUMN change_id INT; -ALTER TABLE nw.contractor ADD COLUMN claim_effect_id INT; -ALTER TABLE nw.contractor ADD CONSTRAINT contractor_uniq UNIQUE (party_id, sequence_id, change_id, claim_effect_id); \ No newline at end of file diff --git a/src/main/resources/db/migration/V49__1.0.67_remove_rec_paytool_event_id.sql b/src/main/resources/db/migration/V49__1.0.67_remove_rec_paytool_event_id.sql deleted file mode 100644 index cdc2c1e0..00000000 --- a/src/main/resources/db/migration/V49__1.0.67_remove_rec_paytool_event_id.sql +++ /dev/null @@ -1,3 +0,0 @@ -DROP INDEX recurrent_payment_tool_event_id_idx; -ALTER TABLE nw.recurrent_payment_tool DROP COLUMN event_id; - diff --git a/src/main/resources/db/migration/V4__1.0.6_optimize_cash_flow_aggregate_functions.sql b/src/main/resources/db/migration/V4__1.0.6_optimize_cash_flow_aggregate_functions.sql deleted file mode 100644 index 58598c2e..00000000 --- a/src/main/resources/db/migration/V4__1.0.6_optimize_cash_flow_aggregate_functions.sql +++ /dev/null @@ -1,517 +0,0 @@ -create or replace function nw.get_cashflow_sum(_cash_flow nw.cash_flow, obj_type nw.payment_change_type, source_account_type nw.cash_flow_account, source_account_type_values varchar[], destination_account_type nw.cash_flow_account, destination_account_type_values varchar[]) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return ( - coalesce( - ( - select amount from (select ($1).*) as cash_flow - where cash_flow.obj_type = $2 - and cash_flow.source_account_type = $3 - and cash_flow.source_account_type_value = ANY ($4) - and cash_flow.destination_account_type = $5 - and cash_flow.destination_account_type_value = ANY ($6) - and ( - (cash_flow.obj_type = 'adjustment' and cash_flow.adj_flow_type = 'new_cash_flow') - or (cash_flow.obj_type != 'adjustment' and cash_flow.adj_flow_type is null) - ) - ), 0) - ); -end; -$$; - -create or replace function nw.cashflow_sum_finalfunc(amount bigint) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return amount; -end; -$$; - -create or replace function nw.get_payment_amount_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'payment'::nw.payment_change_type, - 'provider'::nw.cash_flow_account, - '{"settlement"}', - 'merchant'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -drop aggregate nw.get_payment_amount(nw.cash_flow); -create aggregate nw.get_payment_amount(nw.cash_flow) -( - sfunc = nw.get_payment_amount_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - -create or replace function nw.get_payment_fee_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'payment'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"settlement"}', - 'system'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -drop aggregate nw.get_payment_fee(nw.cash_flow); -create aggregate nw.get_payment_fee(nw.cash_flow) -( - sfunc = nw.get_payment_fee_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - -create or replace function nw.get_payment_external_fee_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'payment'::nw.payment_change_type, - 'system'::nw.cash_flow_account, - '{"settlement"}', - 'external'::nw.cash_flow_account, - '{"income", "outcome"}' - ) - ); -end; -$$; - -drop aggregate nw.get_payment_external_fee(nw.cash_flow); -create aggregate nw.get_payment_external_fee(nw.cash_flow) -( - sfunc = nw.get_payment_external_fee_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - -create or replace function nw.get_payment_provider_fee_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'payment'::nw.payment_change_type, - 'system'::nw.cash_flow_account, - '{"settlement"}', - 'provider'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -drop aggregate nw.get_payment_provider_fee(nw.cash_flow); -create aggregate nw.get_payment_provider_fee(nw.cash_flow) -( - sfunc = nw.get_payment_provider_fee_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - -create or replace function nw.get_payment_guarantee_deposit_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'payment'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"settlement"}', - 'merchant'::nw.cash_flow_account, - '{"guarantee"}' - ) - ); -end; -$$; - -drop aggregate nw.get_payment_guarantee_deposit(nw.cash_flow); -create aggregate nw.get_payment_guarantee_deposit(nw.cash_flow) -( - sfunc = nw.get_payment_guarantee_deposit_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - -create or replace function nw.get_refund_amount_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'refund'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"settlement"}', - 'provider'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -drop aggregate nw.get_refund_amount(nw.cash_flow); -create aggregate nw.get_refund_amount(nw.cash_flow) -( - sfunc = nw.get_refund_amount_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - -create or replace function nw.get_refund_fee_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'refund'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"settlement"}', - 'system'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -drop aggregate nw.get_refund_fee(nw.cash_flow); -create aggregate nw.get_refund_fee(nw.cash_flow) -( - sfunc = nw.get_refund_fee_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - -create or replace function nw.get_refund_external_fee_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'refund'::nw.payment_change_type, - 'system'::nw.cash_flow_account, - '{"settlement"}', - 'external'::nw.cash_flow_account, - '{"income", "outcome"}' - ) - ); -end; -$$; - -drop aggregate nw.get_refund_external_fee(nw.cash_flow); -create aggregate nw.get_refund_external_fee(nw.cash_flow) -( - sfunc = nw.get_refund_external_fee_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - -create or replace function nw.get_refund_provider_fee_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'refund'::nw.payment_change_type, - 'system'::nw.cash_flow_account, - '{"settlement"}', - 'provider'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -drop aggregate nw.get_refund_provider_fee(nw.cash_flow); -create aggregate nw.get_refund_provider_fee(nw.cash_flow) -( - sfunc = nw.get_refund_provider_fee_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - -create or replace function nw.get_payout_amount_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'payout'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"settlement"}', - 'merchant'::nw.cash_flow_account, - '{"payout"}' - ) - ); -end; -$$; - -drop aggregate nw.get_payout_amount(nw.cash_flow); -create aggregate nw.get_payout_amount(nw.cash_flow) -( - sfunc = nw.get_payout_amount_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - -create or replace function nw.get_payout_fee_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'payout'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"settlement"}', - 'system'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -drop aggregate nw.get_payout_fee(nw.cash_flow); -create aggregate nw.get_payout_fee(nw.cash_flow) -( - sfunc = nw.get_payout_fee_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - - -create or replace function nw.get_payout_fixed_fee_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'payout'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"payout"}', - 'system'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -drop aggregate nw.get_payout_fixed_fee(nw.cash_flow); -create aggregate nw.get_payout_fixed_fee(nw.cash_flow) -( - sfunc = nw.get_payout_fixed_fee_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - -create or replace function nw.get_adjustment_amount_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'adjustment'::nw.payment_change_type, - 'provider'::nw.cash_flow_account, - '{"settlement"}', - 'merchant'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -drop aggregate nw.get_adjustment_amount(nw.cash_flow); -create aggregate nw.get_adjustment_amount(nw.cash_flow) -( - sfunc = nw.get_adjustment_amount_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - -create or replace function nw.get_adjustment_fee_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'adjustment'::nw.payment_change_type, - 'merchant'::nw.cash_flow_account, - '{"settlement"}', - 'system'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -drop aggregate nw.get_adjustment_fee(nw.cash_flow); -create aggregate nw.get_adjustment_fee(nw.cash_flow) -( - sfunc = nw.get_adjustment_fee_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - -create or replace function nw.get_adjustment_external_fee_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'adjustment'::nw.payment_change_type, - 'system'::nw.cash_flow_account, - '{"settlement"}', - 'external'::nw.cash_flow_account, - '{"income", "outcome"}' - ) - ); -end; -$$; - -drop aggregate nw.get_adjustment_external_fee(nw.cash_flow); -create aggregate nw.get_adjustment_external_fee(nw.cash_flow) -( - sfunc = nw.get_adjustment_external_fee_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - -create or replace function nw.get_adjustment_provider_fee_sfunc(amount bigint, cash_flow nw.cash_flow) -returns bigint -language plpgsql -immutable -parallel safe -as $$ -begin - return $1 + ( - nw.get_cashflow_sum( - $2, - 'adjustment'::nw.payment_change_type, - 'system'::nw.cash_flow_account, - '{"settlement"}', - 'provider'::nw.cash_flow_account, - '{"settlement"}' - ) - ); -end; -$$; - -drop aggregate nw.get_adjustment_provider_fee(nw.cash_flow); -create aggregate nw.get_adjustment_provider_fee(nw.cash_flow) -( - sfunc = nw.get_adjustment_provider_fee_sfunc, - stype = bigint, - finalfunc = cashflow_sum_finalfunc, - parallel = safe, - initcond = 0 -); - diff --git a/src/main/resources/db/migration/V50__1.0.68_add_revision.sql b/src/main/resources/db/migration/V50__1.0.68_add_revision.sql deleted file mode 100644 index bebe6de3..00000000 --- a/src/main/resources/db/migration/V50__1.0.68_add_revision.sql +++ /dev/null @@ -1,8 +0,0 @@ -ALTER TABLE nw.shop DROP CONSTRAINT shop_uniq -, ADD CONSTRAINT shop_uniq UNIQUE(party_id, sequence_id, change_id, claim_effect_id, revision); - -ALTER TABLE nw.contract DROP CONSTRAINT contract_uniq -, ADD CONSTRAINT contract_uniq UNIQUE(party_id, sequence_id, change_id, claim_effect_id, revision); - -ALTER TABLE nw.contractor DROP CONSTRAINT contractor_uniq -, ADD CONSTRAINT contractor_uniq UNIQUE(party_id, sequence_id, change_id, claim_effect_id, revision); \ No newline at end of file diff --git a/src/main/resources/db/migration/V51__cntrct_seq.sql b/src/main/resources/db/migration/V51__cntrct_seq.sql deleted file mode 100644 index 4d4bcd6a..00000000 --- a/src/main/resources/db/migration/V51__cntrct_seq.sql +++ /dev/null @@ -1,5 +0,0 @@ -CREATE SEQUENCE nw.cntrct_seq - INCREMENT 1 - START 2000000 - MINVALUE 2000000 - CACHE 1; \ No newline at end of file diff --git a/src/main/resources/db/migration/V52__drop_index.sql b/src/main/resources/db/migration/V52__drop_index.sql deleted file mode 100644 index 8d343c81..00000000 --- a/src/main/resources/db/migration/V52__drop_index.sql +++ /dev/null @@ -1,11 +0,0 @@ -DROP INDEX party_event_id; -ALTER TABLE nw.party DROP COLUMN event_id; - -DROP INDEX shop_event_id; -ALTER TABLE nw.shop DROP COLUMN event_id; - -DROP INDEX contract_event_id; -ALTER TABLE nw.contract DROP COLUMN event_id; - -DROP INDEX contractor_event_id; -ALTER TABLE nw.contractor DROP COLUMN event_id; \ No newline at end of file diff --git a/src/main/resources/db/migration/V53__drop_fk_party.sql b/src/main/resources/db/migration/V53__drop_fk_party.sql deleted file mode 100644 index ee0cec12..00000000 --- a/src/main/resources/db/migration/V53__drop_fk_party.sql +++ /dev/null @@ -1,2 +0,0 @@ -alter table nw.contract_adjustment drop constraint if exists fk_adjustment_to_contract; -alter table nw.payout_tool drop constraint if exists fk_payout_tool_to_contract; \ No newline at end of file diff --git a/src/main/resources/db/migration/V54__add_ids_contr.sql b/src/main/resources/db/migration/V54__add_ids_contr.sql deleted file mode 100644 index 3b0f7b9d..00000000 --- a/src/main/resources/db/migration/V54__add_ids_contr.sql +++ /dev/null @@ -1,8 +0,0 @@ -ALTER TABLE nw.shop DROP CONSTRAINT shop_uniq -, ADD CONSTRAINT shop_uniq UNIQUE(party_id, shop_id, sequence_id, change_id, claim_effect_id, revision); - -ALTER TABLE nw.contract DROP CONSTRAINT contract_uniq -, ADD CONSTRAINT contract_uniq UNIQUE(party_id, contract_id, sequence_id, change_id, claim_effect_id, revision); - -ALTER TABLE nw.contractor DROP CONSTRAINT contractor_uniq -, ADD CONSTRAINT contractor_uniq UNIQUE(party_id, contractor_id, sequence_id, change_id, claim_effect_id, revision); \ No newline at end of file diff --git a/src/main/resources/db/migration/V55__drop_cntrct_seq.sql b/src/main/resources/db/migration/V55__drop_cntrct_seq.sql deleted file mode 100644 index 5d4e0f5e..00000000 --- a/src/main/resources/db/migration/V55__drop_cntrct_seq.sql +++ /dev/null @@ -1 +0,0 @@ -DROP SEQUENCE nw.cntrct_seq; \ No newline at end of file diff --git a/src/main/resources/db/migration/V56__revision_tables.sql b/src/main/resources/db/migration/V56__revision_tables.sql deleted file mode 100644 index 13702f0b..00000000 --- a/src/main/resources/db/migration/V56__revision_tables.sql +++ /dev/null @@ -1,39 +0,0 @@ -ALTER TABLE nw.shop DROP CONSTRAINT shop_uniq -, ADD CONSTRAINT shop_uniq UNIQUE(party_id, shop_id, sequence_id, change_id, claim_effect_id); - -ALTER TABLE nw.contract DROP CONSTRAINT contract_uniq -, ADD CONSTRAINT contract_uniq UNIQUE(party_id, contract_id, sequence_id, change_id, claim_effect_id); - -ALTER TABLE nw.contractor DROP CONSTRAINT contractor_uniq -, ADD CONSTRAINT contractor_uniq UNIQUE(party_id, contractor_id, sequence_id, change_id, claim_effect_id); - -ALTER TABLE nw.shop DROP COLUMN revision; -ALTER TABLE nw.contract DROP COLUMN revision; -ALTER TABLE nw.contractor DROP COLUMN revision; - -CREATE TABLE nw.shop_revision( - id BIGSERIAL NOT NULL, - obj_id BIGINT NOT NULL, - revision BIGINT NOT NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - CONSTRAINT shop_revision_pkey PRIMARY KEY (id) -); -CREATE UNIQUE INDEX shop_revision_idx on nw.shop_revision(obj_id, revision); - -CREATE TABLE nw.contract_revision( - id BIGSERIAL NOT NULL, - obj_id BIGINT NOT NULL, - revision BIGINT NOT NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - CONSTRAINT contract_revision_pkey PRIMARY KEY (id) -); -CREATE UNIQUE INDEX contract_revision_idx on nw.contract_revision(obj_id, revision); - -CREATE TABLE nw.contractor_revision( - id BIGSERIAL NOT NULL, - obj_id BIGINT NOT NULL, - revision BIGINT NOT NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - CONSTRAINT contractor_revision_pkey PRIMARY KEY (id) -); -CREATE UNIQUE INDEX contractor_revision_idx on nw.contractor_revision(obj_id, revision); \ No newline at end of file diff --git a/src/main/resources/db/migration/V57.1__add_chargeback.sql b/src/main/resources/db/migration/V57.1__add_chargeback.sql deleted file mode 100644 index a3263e3d..00000000 --- a/src/main/resources/db/migration/V57.1__add_chargeback.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TYPE nw.payment_change_type ADD VALUE 'chargeback'; diff --git a/src/main/resources/db/migration/V57.2__add_chargeback.sql b/src/main/resources/db/migration/V57.2__add_chargeback.sql deleted file mode 100644 index bdc2a448..00000000 --- a/src/main/resources/db/migration/V57.2__add_chargeback.sql +++ /dev/null @@ -1,44 +0,0 @@ -CREATE TYPE nw.chargeback_status AS ENUM ('pending', 'accepted', 'rejected', 'cancelled'); - -CREATE TYPE nw.chargeback_category AS ENUM ('fraud', 'dispute', 'authorisation', 'processing_error'); - -CREATE TYPE nw.chargeback_stage AS ENUM ('chargeback', 'pre_arbitration', 'arbitration'); - -CREATE TABLE nw.chargeback -( - id BIGSERIAL NOT NULL, - sequence_id BIGINT NOT NULL, - change_id INT NOT NULL, - domain_revision BIGINT NOT NULL, - party_revision BIGINT, - chargeback_id CHARACTER VARYING NOT NULL, - payment_id CHARACTER VARYING NOT NULL, - invoice_id CHARACTER VARYING NOT NULL, - shop_id CHARACTER VARYING NOT NULL, - party_id CHARACTER VARYING NOT NULL, - external_id CHARACTER VARYING, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - status nw.chargeback_status NOT NULL, - levy_amount BIGINT, - levy_currency_code CHARACTER VARYING, - amount BIGINT, - currency_code CHARACTER VARYING, - reason_code CHARACTER VARYING, - reason_category nw.chargeback_category NOT NULL, - stage nw.chargeback_stage NOT NULL, - current BOOLEAN NOT NULL DEFAULT TRUE, - context BYTEA, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - - CONSTRAINT chargeback_pkey PRIMARY KEY (id) -); - -ALTER TABLE nw.chargeback - ADD CONSTRAINT chargeback_uniq UNIQUE (invoice_id, sequence_id, change_id); - -CREATE INDEX chargeback_invoice_id on nw.chargeback (invoice_id); -CREATE INDEX chargeback_party_id on nw.chargeback (party_id); -CREATE INDEX chargeback_status on nw.chargeback (status); -CREATE INDEX chargeback_created_at on nw.chargeback (created_at); -CREATE INDEX chargeback_event_created_at on nw.chargeback (event_created_at); diff --git a/src/main/resources/db/migration/V58__drop_f_payment_system_rates.sql b/src/main/resources/db/migration/V58__drop_f_payment_system_rates.sql deleted file mode 100644 index 74c000ab..00000000 --- a/src/main/resources/db/migration/V58__drop_f_payment_system_rates.sql +++ /dev/null @@ -1,4 +0,0 @@ -drop index rate_ukey; -ALTER TABLE nw.rate DROP COLUMN payment_system; - -create unique index rate_ukey on nw.rate(source_id, sequence_id, change_id, source_symbolic_code, destination_symbolic_code); \ No newline at end of file diff --git a/src/main/resources/db/migration/V59__revert_V58.sql b/src/main/resources/db/migration/V59__revert_V58.sql deleted file mode 100644 index 7e9c08bf..00000000 --- a/src/main/resources/db/migration/V59__revert_V58.sql +++ /dev/null @@ -1,4 +0,0 @@ -alter table nw.rate add column payment_system character varying not null default ''; - -drop index rate_ukey; -create unique index rate_ukey on nw.rate(source_id, sequence_id, change_id, source_symbolic_code, destination_symbolic_code, payment_system); \ No newline at end of file diff --git a/src/main/resources/db/migration/V5__domain_objects.sql b/src/main/resources/db/migration/V5__domain_objects.sql deleted file mode 100644 index d04b493c..00000000 --- a/src/main/resources/db/migration/V5__domain_objects.sql +++ /dev/null @@ -1,11 +0,0 @@ -CREATE TABLE nw.category( - id BIGSERIAL NOT NULL, - version_id BIGINT NOT NULL, - category_id INT NOT NULL, - name CHARACTER VARYING NOT NULL, - description CHARACTER VARYING NOT NULL, - type CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT category_pkey PRIMARY KEY (id) -); diff --git a/src/main/resources/db/migration/V60__add_adjustment_cashflow_amount.sql b/src/main/resources/db/migration/V60__add_adjustment_cashflow_amount.sql deleted file mode 100644 index dce3c7fa..00000000 --- a/src/main/resources/db/migration/V60__add_adjustment_cashflow_amount.sql +++ /dev/null @@ -1,17 +0,0 @@ -ALTER TABLE nw.adjustment - ADD amount BIGINT; - -UPDATE nw.adjustment a -SET amount = p.fee - a.fee -FROM nw.payment p -WHERE p.payment_id = a.payment_id - AND p.invoice_id = a.invoice_id - AND p.current; - -ALTER TABLE nw.adjustment - ALTER COLUMN amount SET NOT NULL; - -ALTER TABLE nw.adjustment - DROP COLUMN fee, - DROP COLUMN external_fee, - DROP COLUMN provider_fee; diff --git a/src/main/resources/db/migration/V61__drop_f_payment_system_rates.sql b/src/main/resources/db/migration/V61__drop_f_payment_system_rates.sql deleted file mode 100644 index 5c305a7b..00000000 --- a/src/main/resources/db/migration/V61__drop_f_payment_system_rates.sql +++ /dev/null @@ -1,8 +0,0 @@ -drop index rate_ukey; -ALTER TABLE nw.rate DROP COLUMN payment_system; - -create unique index rate_ukey on nw.rate(source_id, sequence_id, source_symbolic_code, destination_symbolic_code); - -drop index rate_event_id_idx; -ALTER TABLE nw.rate DROP COLUMN event_id; -ALTER TABLE nw.rate DROP COLUMN change_id; \ No newline at end of file diff --git a/src/main/resources/db/migration/V62__new_withdrawal_provider_id.sql b/src/main/resources/db/migration/V62__new_withdrawal_provider_id.sql deleted file mode 100644 index 1689e3b5..00000000 --- a/src/main/resources/db/migration/V62__new_withdrawal_provider_id.sql +++ /dev/null @@ -1,6 +0,0 @@ -alter table nw.withdrawal_session rename column provider_id to provider_id_legacy; -alter table nw.withdrawal_session alter column provider_id_legacy drop not null; -alter table nw.withdrawal_session add column provider_id int; - -alter table nw.withdrawal rename column provider_id to provider_id_legacy; -alter table nw.withdrawal add column provider_id int; diff --git a/src/main/resources/db/migration/V63__optional_abs_account.sql b/src/main/resources/db/migration/V63__optional_abs_account.sql deleted file mode 100644 index 82442bf7..00000000 --- a/src/main/resources/db/migration/V63__optional_abs_account.sql +++ /dev/null @@ -1 +0,0 @@ -alter table nw.provider alter column abs_account drop not null; diff --git a/src/main/resources/db/migration/V64__optional_terminal_json.sql b/src/main/resources/db/migration/V64__optional_terminal_json.sql deleted file mode 100644 index 6b2ff9e9..00000000 --- a/src/main/resources/db/migration/V64__optional_terminal_json.sql +++ /dev/null @@ -1 +0,0 @@ -alter table nw.provider alter column terminal_json drop not null; \ No newline at end of file diff --git a/src/main/resources/db/migration/V65__add_payer_cardholder_name.sql b/src/main/resources/db/migration/V65__add_payer_cardholder_name.sql deleted file mode 100644 index 0346e18e..00000000 --- a/src/main/resources/db/migration/V65__add_payer_cardholder_name.sql +++ /dev/null @@ -1 +0,0 @@ -alter table nw.payment add column payer_bank_card_cardholder_name character varying; diff --git a/src/main/resources/db/migration/V66__unied_provider-n-ext-term.sql b/src/main/resources/db/migration/V66__unied_provider-n-ext-term.sql deleted file mode 100644 index 5abc67d1..00000000 --- a/src/main/resources/db/migration/V66__unied_provider-n-ext-term.sql +++ /dev/null @@ -1,7 +0,0 @@ -ALTER TABLE nw.provider ADD identity CHARACTER VARYING; -ALTER TABLE nw.provider ADD wallet_terms_json CHARACTER VARYING; -ALTER TABLE nw.provider ADD params_schema_json CHARACTER VARYING; - -ALTER TABLE nw.terminal ADD external_terminal_id CHARACTER VARYING; -ALTER TABLE nw.terminal ADD external_merchant_id CHARACTER VARYING; -ALTER TABLE nw.terminal ADD mcc CHARACTER VARYING; diff --git a/src/main/resources/db/migration/V67__add_terminal_provider_ref.sql b/src/main/resources/db/migration/V67__add_terminal_provider_ref.sql deleted file mode 100644 index ab20eb89..00000000 --- a/src/main/resources/db/migration/V67__add_terminal_provider_ref.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE nw.terminal ADD terminal_provider_ref_id INT; diff --git a/src/main/resources/db/migration/V68__remove_not_null_contraint_terminal.sql b/src/main/resources/db/migration/V68__remove_not_null_contraint_terminal.sql deleted file mode 100644 index bab23f35..00000000 --- a/src/main/resources/db/migration/V68__remove_not_null_contraint_terminal.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE nw.terminal ALTER COLUMN risk_coverage DROP NOT NULL; diff --git a/src/main/resources/db/migration/V69__add_charged_back_payment_status.sql b/src/main/resources/db/migration/V69__add_charged_back_payment_status.sql deleted file mode 100644 index 55fff959..00000000 --- a/src/main/resources/db/migration/V69__add_charged_back_payment_status.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TYPE nw.payment_status ADD VALUE 'charged_back'; diff --git a/src/main/resources/db/migration/V6__1.0.10_swift_data.sql b/src/main/resources/db/migration/V6__1.0.10_swift_data.sql deleted file mode 100644 index 149e09c7..00000000 --- a/src/main/resources/db/migration/V6__1.0.10_swift_data.sql +++ /dev/null @@ -1,47 +0,0 @@ -ALTER TABLE nw.payout_tool - ADD COLUMN payout_tool_info_international_bank_number CHARACTER VARYING; -ALTER TABLE nw.payout_tool - ADD COLUMN payout_tool_info_international_bank_aba_rtn CHARACTER VARYING; -ALTER TABLE nw.payout_tool - ADD COLUMN payout_tool_info_international_bank_country_code CHARACTER VARYING; - -ALTER TABLE nw.payout_tool - ADD COLUMN payout_tool_info_international_correspondent_bank_account CHARACTER VARYING; -ALTER TABLE nw.payout_tool - ADD COLUMN payout_tool_info_international_correspondent_bank_name CHARACTER VARYING; -ALTER TABLE nw.payout_tool - ADD COLUMN payout_tool_info_international_correspondent_bank_address CHARACTER VARYING; -ALTER TABLE nw.payout_tool - ADD COLUMN payout_tool_info_international_correspondent_bank_bic CHARACTER VARYING; -ALTER TABLE nw.payout_tool - ADD COLUMN payout_tool_info_international_correspondent_bank_iban CHARACTER VARYING; - ALTER TABLE nw.payout_tool - ADD COLUMN payout_tool_info_international_correspondent_bank_number CHARACTER VARYING; -ALTER TABLE nw.payout_tool - ADD COLUMN payout_tool_info_international_correspondent_bank_aba_rtn CHARACTER VARYING; -ALTER TABLE nw.payout_tool - ADD COLUMN payout_tool_info_international_correspondent_bank_country_code CHARACTER VARYING; - -ALTER TABLE nw.payout - ADD COLUMN type_account_international_bank_number CHARACTER VARYING; -ALTER TABLE nw.payout - ADD COLUMN type_account_international_bank_aba_rtn CHARACTER VARYING; -ALTER TABLE nw.payout - ADD COLUMN type_account_international_bank_country_code CHARACTER VARYING; - -ALTER TABLE nw.payout - ADD COLUMN type_account_international_correspondent_bank_number CHARACTER VARYING; -ALTER TABLE nw.payout - ADD COLUMN type_account_international_correspondent_bank_account CHARACTER VARYING; -ALTER TABLE nw.payout - ADD COLUMN type_account_international_correspondent_bank_name CHARACTER VARYING; -ALTER TABLE nw.payout - ADD COLUMN type_account_international_correspondent_bank_address CHARACTER VARYING; -ALTER TABLE nw.payout - ADD COLUMN type_account_international_correspondent_bank_bic CHARACTER VARYING; -ALTER TABLE nw.payout - ADD COLUMN type_account_international_correspondent_bank_iban CHARACTER VARYING; -ALTER TABLE nw.payout - ADD COLUMN type_account_international_correspondent_bank_aba_rtn CHARACTER VARYING; -ALTER TABLE nw.payout - ADD COLUMN type_account_international_correspondent_bank_country_code CHARACTER VARYING; \ No newline at end of file diff --git a/src/main/resources/db/migration/V71__add_payment_routing_rules_table.sql b/src/main/resources/db/migration/V71__add_payment_routing_rules_table.sql deleted file mode 100644 index fd9f035e..00000000 --- a/src/main/resources/db/migration/V71__add_payment_routing_rules_table.sql +++ /dev/null @@ -1,12 +0,0 @@ -CREATE TABLE nw.payment_routing_rule ( - id BIGSERIAL NOT NULL, - rule_id INTEGER NOT NULL, - name CHARACTER VARYING NOT NULL, - description CHARACTER VARYING, - routing_decisions_jsonb JSONB NOT NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT payment_routing_rule_pkey PRIMARY KEY (id) -); - -CREATE INDEX payment_routing_rule_ref_id on nw.payment_routing_rule(rule_id); \ No newline at end of file diff --git a/src/main/resources/db/migration/V72__change_type_for_routing_decisions_column.sql b/src/main/resources/db/migration/V72__change_type_for_routing_decisions_column.sql deleted file mode 100644 index 5661aa59..00000000 --- a/src/main/resources/db/migration/V72__change_type_for_routing_decisions_column.sql +++ /dev/null @@ -1,2 +0,0 @@ -alter table nw.payment_routing_rule drop column routing_decisions_jsonb; -alter table nw.payment_routing_rule add column routing_decisions_json character varying not null; diff --git a/src/main/resources/db/migration/V73__rename_rule_id_and_add_version_column.sql b/src/main/resources/db/migration/V73__rename_rule_id_and_add_version_column.sql deleted file mode 100644 index e79124ef..00000000 --- a/src/main/resources/db/migration/V73__rename_rule_id_and_add_version_column.sql +++ /dev/null @@ -1,2 +0,0 @@ -alter table nw.payment_routing_rule add column version_id bigint not null; -alter table nw.payment_routing_rule rename column rule_id to rule_ref_id; diff --git a/src/main/resources/db/migration/V74__change_fileds_for_new_seq_scheme.sql b/src/main/resources/db/migration/V74__change_fileds_for_new_seq_scheme.sql deleted file mode 100644 index bf1775c9..00000000 --- a/src/main/resources/db/migration/V74__change_fileds_for_new_seq_scheme.sql +++ /dev/null @@ -1,34 +0,0 @@ -drop index deposit_event_id_idx; -ALTER TABLE nw.deposit DROP COLUMN event_id; -ALTER TABLE nw.deposit ADD CONSTRAINT deposit_uniq UNIQUE(deposit_id, sequence_id); - -drop INDEX destination_event_id_idx; -ALTER TABLE nw.destination DROP COLUMN event_id; -ALTER TABLE nw.destination ADD CONSTRAINT destination_uniq UNIQUE(destination_id, sequence_id); - -drop INDEX identity_event_id_idx; -ALTER TABLE nw.identity DROP COLUMN event_id; -ALTER TABLE nw.identity ADD CONSTRAINT identity_uniq UNIQUE(identity_id, sequence_id); - -drop INDEX challenge_event_id_idx; -ALTER TABLE nw.challenge DROP COLUMN event_id; -ALTER TABLE nw.challenge ADD CONSTRAINT challenge_uniq UNIQUE(challenge_id, identity_id, sequence_id); - -drop INDEX source_event_id_idx; -ALTER TABLE nw.source DROP COLUMN event_id; -ALTER TABLE nw.source ADD CONSTRAINT source_uniq UNIQUE(source_id, sequence_id); - -drop INDEX wallet_event_id_idx; -ALTER TABLE nw.wallet DROP COLUMN event_id; -ALTER TABLE nw.wallet ADD CONSTRAINT wallet_uniq UNIQUE(wallet_id, sequence_id); - -drop INDEX withdrawal_event_id_idx; -ALTER TABLE nw.withdrawal DROP COLUMN event_id; -ALTER TABLE nw.withdrawal ADD CONSTRAINT withdrawal_uniq UNIQUE(withdrawal_id, sequence_id); - -drop INDEX withdrawal_session_event_id_idx; -ALTER TABLE nw.withdrawal_session DROP COLUMN event_id; -ALTER TABLE nw.withdrawal_session ADD CONSTRAINT withdrawal_session_uniq UNIQUE(withdrawal_session_id, sequence_id); - -ALTER TABLE nw.payout ADD COLUMN change_id INT not null; -ALTER TABLE nw.payout ADD CONSTRAINT payout_uniq UNIQUE(event_id, payout_id, change_id); diff --git a/src/main/resources/db/migration/V75__add_new_payment_systems.sql b/src/main/resources/db/migration/V75__add_new_payment_systems.sql deleted file mode 100644 index f91b94cc..00000000 --- a/src/main/resources/db/migration/V75__add_new_payment_systems.sql +++ /dev/null @@ -1,3 +0,0 @@ -alter type nw.BANK_CARD_PAYMENT_SYSTEM add value 'elo'; -alter type nw.BANK_CARD_PAYMENT_SYSTEM add value 'rupay'; -alter type nw.BANK_CARD_PAYMENT_SYSTEM add value 'ebt'; diff --git a/src/main/resources/db/migration/V76__add_uzcard_payment_system.sql b/src/main/resources/db/migration/V76__add_uzcard_payment_system.sql deleted file mode 100644 index 03e3549d..00000000 --- a/src/main/resources/db/migration/V76__add_uzcard_payment_system.sql +++ /dev/null @@ -1 +0,0 @@ -alter type nw.BANK_CARD_PAYMENT_SYSTEM add value 'uzcard'; diff --git a/src/main/resources/db/migration/V77__add_deposit_revert_type.sql b/src/main/resources/db/migration/V77__add_deposit_revert_type.sql deleted file mode 100644 index f73199c7..00000000 --- a/src/main/resources/db/migration/V77__add_deposit_revert_type.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TYPE nw.fistful_cash_flow_change_type ADD VALUE 'deposit_revert'; \ No newline at end of file diff --git a/src/main/resources/db/migration/V78__add_deposit_adjustment_type.sql b/src/main/resources/db/migration/V78__add_deposit_adjustment_type.sql deleted file mode 100644 index 29b31485..00000000 --- a/src/main/resources/db/migration/V78__add_deposit_adjustment_type.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TYPE nw.fistful_cash_flow_change_type ADD VALUE 'deposit_adjustment'; \ No newline at end of file diff --git a/src/main/resources/db/migration/V79__revert_and_adjustment_deposit_tables.sql b/src/main/resources/db/migration/V79__revert_and_adjustment_deposit_tables.sql deleted file mode 100644 index f48deac3..00000000 --- a/src/main/resources/db/migration/V79__revert_and_adjustment_deposit_tables.sql +++ /dev/null @@ -1,56 +0,0 @@ -CREATE TYPE nw.deposit_revert_status AS ENUM ('pending', 'succeeded', 'failed'); -CREATE TYPE nw.deposit_adjustment_status AS ENUM ('pending', 'succeeded'); - -CREATE TABLE nw.deposit_revert ( - id BIGSERIAL NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - event_occured_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - sequence_id INT NOT NULL, - source_id CHARACTER VARYING NOT NULL, - wallet_id CHARACTER VARYING NOT NULL, - deposit_id CHARACTER VARYING NOT NULL, - revert_id CHARACTER VARYING NOT NULL, - amount BIGINT NOT NULL, - fee BIGINT, - provider_fee BIGINT, - currency_code CHARACTER VARYING NOT NULL, - status nw.deposit_revert_status NOT NULL, - transfer_status nw.deposit_transfer_status, - reason CHARACTER VARYING, - external_id CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT deposit_revert_pkey PRIMARY KEY (id) -); - -CREATE INDEX deposit_revert_event_created_at_idx on nw.deposit_revert (event_created_at); -CREATE INDEX deposit_revert_id_idx on nw.deposit_revert (deposit_id); -ALTER TABLE nw.deposit_revert ADD CONSTRAINT deposit_revert_uniq UNIQUE(deposit_id, revert_id, sequence_id); - - -CREATE TABLE nw.deposit_adjustment ( - id BIGSERIAL NOT NULL, - event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - event_occured_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - sequence_id INT NOT NULL, - source_id CHARACTER VARYING NOT NULL, - wallet_id CHARACTER VARYING NOT NULL, - deposit_id CHARACTER VARYING NOT NULL, - adjustment_id CHARACTER VARYING NOT NULL, - amount BIGINT NOT NULL, - fee BIGINT, - provider_fee BIGINT, - currency_code CHARACTER VARYING NOT NULL, - status nw.deposit_adjustment_status NOT NULL, - transfer_status nw.deposit_transfer_status, - deposit_status nw.deposit_status, - external_id CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT deposit_adjustment_pkey PRIMARY KEY (id) -); - -CREATE INDEX deposit_adjustment_event_created_at_idx on nw.deposit_adjustment (event_created_at); -CREATE INDEX deposit_adjustment_id_idx on nw.deposit_adjustment (deposit_id); -ALTER TABLE nw.deposit_adjustment ADD CONSTRAINT deposit_adjustment_uniq UNIQUE(deposit_id, adjustment_id, sequence_id); - diff --git a/src/main/resources/db/migration/V7__domain_objects.sql b/src/main/resources/db/migration/V7__domain_objects.sql deleted file mode 100644 index 74b63135..00000000 --- a/src/main/resources/db/migration/V7__domain_objects.sql +++ /dev/null @@ -1,199 +0,0 @@ -DROP TABLE nw.category; ---category-- -CREATE TABLE nw.category( - id BIGSERIAL NOT NULL, - version_id BIGINT NOT NULL, - category_ref_id INT NOT NULL, - name CHARACTER VARYING NOT NULL, - description CHARACTER VARYING NOT NULL, - type CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT category_pkey PRIMARY KEY (id) -); - -CREATE INDEX category_version_id on nw.category(version_id); -CREATE INDEX category_idx on nw.category(category_ref_id); - ---currency-- -CREATE TABLE nw.currency( - id BIGSERIAL NOT NULL, - version_id BIGINT NOT NULL, - currency_ref_id CHARACTER VARYING NOT NULL, - name CHARACTER VARYING NOT NULL, - symbolic_code CHARACTER VARYING NOT NULL, - numeric_code SMALLINT NOT NULL, - exponent SMALLINT NOT NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT currency_pkey PRIMARY KEY (id) -); - -CREATE INDEX currency_version_id on nw.currency(version_id); -CREATE INDEX currency_idx on nw.currency(currency_ref_id); - ---calendar-- -CREATE TABLE nw.calendar( - id BIGSERIAL NOT NULL, - version_id BIGINT NOT NULL, - calendar_ref_id INT NOT NULL, - name CHARACTER VARYING NOT NULL, - description CHARACTER VARYING, - timezone CHARACTER VARYING NOT NULL, - holidays_json CHARACTER VARYING NOT NULL, - first_day_of_week INT, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT calendar_pkey PRIMARY KEY (id) -); - -CREATE INDEX calendar_version_id on nw.calendar(version_id); -CREATE INDEX calendar_idx on nw.calendar(calendar_ref_id); - ---provider-- -CREATE TABLE nw.provider( - id BIGSERIAL NOT NULL, - version_id BIGINT NOT NULL, - provider_ref_id INT NOT NULL, - name CHARACTER VARYING NOT NULL, - description CHARACTER VARYING NOT NULL, - proxy_ref_id INT NOT NULL, - proxy_additional_json CHARACTER VARYING NOT NULL, - terminal_json CHARACTER VARYING NOT NULL, - abs_account CHARACTER VARYING NOT NULL, - payment_terms_json CHARACTER VARYING, - recurrent_paytool_terms_json CHARACTER VARYING, - accounts_json CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT provider_pkey PRIMARY KEY (id) -); - -CREATE INDEX provider_version_id on nw.provider(version_id); -CREATE INDEX provider_idx on nw.provider(provider_ref_id); - ---terminal-- -CREATE TABLE nw.terminal( - id BIGSERIAL NOT NULL, - version_id BIGINT NOT NULL, - terminal_ref_id INT NOT NULL, - name CHARACTER VARYING NOT NULL, - description CHARACTER VARYING NOT NULL, - options_json CHARACTER VARYING, - risk_coverage CHARACTER VARYING NOT NULL, - terms_json CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT terminal_pkey PRIMARY KEY (id) -); - -CREATE INDEX terminal_version_id on nw.terminal(version_id); -CREATE INDEX terminal_idx on nw.terminal(terminal_ref_id); - ---payment_method-- -CREATE TYPE nw.payment_method_type AS ENUM('bank_card', 'payment_terminal', 'digital_wallet', 'tokenized_bank_card'); - -CREATE TABLE nw.payment_method( - id BIGSERIAL NOT NULL, - version_id BIGINT NOT NULL, - payment_method_ref_id CHARACTER VARYING NOT NULL, - name CHARACTER VARYING NOT NULL, - description CHARACTER VARYING NOT NULL, - type nw.payment_method_type NOT NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT payment_method_pkey PRIMARY KEY (id) -); - -CREATE INDEX payment_method_version_id on nw.payment_method(version_id); -CREATE INDEX payment_method_idx on nw.payment_method(payment_method_ref_id); - ---payout_method-- -CREATE TABLE nw.payout_method( - id BIGSERIAL NOT NULL, - version_id BIGINT NOT NULL, - payout_method_ref_id CHARACTER VARYING NOT NULL, - name CHARACTER VARYING NOT NULL, - description CHARACTER VARYING NOT NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT payout_method_pkey PRIMARY KEY (id) -); - -CREATE INDEX payout_method_version_id on nw.payout_method(version_id); -CREATE INDEX payout_method_idx on nw.payout_method(payout_method_ref_id); - ---payment_institution-- -CREATE TABLE nw.payment_institution( - id BIGSERIAL NOT NULL, - version_id BIGINT NOT NULL, - payment_institution_ref_id INT NOT NULL, - name CHARACTER VARYING NOT NULL, - description CHARACTER VARYING, - calendar_ref_id INT, - system_account_set_json CHARACTER VARYING NOT NULL, - default_contract_template_json CHARACTER VARYING NOT NULL, - default_wallet_contract_template_json CHARACTER VARYING, - providers_json CHARACTER VARYING NOT NULL, - inspector_json CHARACTER VARYING NOT NULL, - realm CHARACTER VARYING NOT NULL, - residences_json CHARACTER VARYING NOT NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT payment_institution_pkey PRIMARY KEY (id) -); - -CREATE INDEX payment_institution_version_id on nw.payment_institution(version_id); -CREATE INDEX payment_institution_idx on nw.payment_institution(payment_institution_ref_id); - ---inspector-- -CREATE TABLE nw.inspector( - id BIGSERIAL NOT NULL, - version_id BIGINT NOT NULL, - inspector_ref_id INT NOT NULL, - name CHARACTER VARYING NOT NULL, - description CHARACTER VARYING NOT NULL, - proxy_ref_id INT NOT NULL, - proxy_additional_json CHARACTER VARYING NOT NULL, - fallback_risk_score CHARACTER VARYING, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT inspector_pkey PRIMARY KEY (id) -); - -CREATE INDEX inspector_version_id on nw.inspector(version_id); -CREATE INDEX inspector_idx on nw.inspector(inspector_ref_id); - ---proxy-- -CREATE TABLE nw.proxy( - id BIGSERIAL NOT NULL, - version_id BIGINT NOT NULL, - proxy_ref_id INT NOT NULL, - name CHARACTER VARYING NOT NULL, - description CHARACTER VARYING NOT NULL, - url CHARACTER VARYING NOT NULL, - options_json CHARACTER VARYING NOT NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT proxy_pkey PRIMARY KEY (id) -); - -CREATE INDEX proxy_version_id on nw.proxy(version_id); -CREATE INDEX proxy_idx on nw.proxy(proxy_ref_id); - ---term_set_hierarchy-- -CREATE TABLE nw.term_set_hierarchy( - id BIGSERIAL NOT NULL, - version_id BIGINT NOT NULL, - term_set_hierarchy_ref_id INT NOT NULL, - name CHARACTER VARYING, - description CHARACTER VARYING, - parent_terms_ref_id INT, - term_sets_json CHARACTER VARYING NOT NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT term_set_hierarchy_pkey PRIMARY KEY (id) -); - -CREATE INDEX term_set_hierarchy_version_id on nw.term_set_hierarchy(version_id); -CREATE INDEX term_set_hierarchy_idx on nw.term_set_hierarchy(term_set_hierarchy_ref_id); \ No newline at end of file diff --git a/src/main/resources/db/migration/V80__change_payment_method_ids.sql b/src/main/resources/db/migration/V80__change_payment_method_ids.sql deleted file mode 100644 index 6a3190b9..00000000 --- a/src/main/resources/db/migration/V80__change_payment_method_ids.sql +++ /dev/null @@ -1,2 +0,0 @@ -UPDATE nw.payment_method -SET payment_method_ref_id = type || '.' || payment_method_ref_id; \ No newline at end of file diff --git a/src/main/resources/db/migration/V81__fix_deposit_adjustment_and_revert.sql b/src/main/resources/db/migration/V81__fix_deposit_adjustment_and_revert.sql deleted file mode 100644 index 8e796116..00000000 --- a/src/main/resources/db/migration/V81__fix_deposit_adjustment_and_revert.sql +++ /dev/null @@ -1,17 +0,0 @@ -ALTER TABLE nw.deposit_adjustment - ALTER COLUMN amount DROP NOT NULL; - -ALTER TABLE nw.deposit_adjustment - ALTER COLUMN currency_code DROP NOT NULL; - -ALTER TABLE nw.deposit_adjustment - ADD COLUMN party_revision BIGINT NOT NULL DEFAULT 0; - -ALTER TABLE nw.deposit_adjustment - ADD COLUMN domain_revision BIGINT NOT NULL DEFAULT 0; - -ALTER TABLE nw.deposit_revert - ADD COLUMN party_revision BIGINT NOT NULL DEFAULT 0; - -ALTER TABLE nw.deposit_revert - ADD COLUMN domain_revision BIGINT NOT NULL DEFAULT 0; diff --git a/src/main/resources/db/migration/V82__add_crypto_currency_deprecated_type.sql b/src/main/resources/db/migration/V82__add_crypto_currency_deprecated_type.sql deleted file mode 100644 index 954e5ec7..00000000 --- a/src/main/resources/db/migration/V82__add_crypto_currency_deprecated_type.sql +++ /dev/null @@ -1 +0,0 @@ -alter type nw.payment_tool_type add value if not exists 'crypto_currency_deprecated'; diff --git a/src/main/resources/db/migration/V83__change_mobile_operator_type.sql b/src/main/resources/db/migration/V83__change_mobile_operator_type.sql deleted file mode 100644 index 0a89955e..00000000 --- a/src/main/resources/db/migration/V83__change_mobile_operator_type.sql +++ /dev/null @@ -1,5 +0,0 @@ -alter table nw.recurrent_payment_tool rename column mobile_commerce_operator to mobile_commerce_operator_legacy; -alter table nw.recurrent_payment_tool add column mobile_commerce_operator character varying; - -alter table nw.payment rename column payer_mobile_operator to payer_mobile_operator_legacy; -alter table nw.payment add column payer_mobile_operator character varying; diff --git a/src/main/resources/db/migration/V84__add_country_and_trade_bloc.sql b/src/main/resources/db/migration/V84__add_country_and_trade_bloc.sql deleted file mode 100644 index 13235d8a..00000000 --- a/src/main/resources/db/migration/V84__add_country_and_trade_bloc.sql +++ /dev/null @@ -1,29 +0,0 @@ ---contractor-- -ALTER TABLE nw.contractor - ADD COLUMN international_legal_entity_country_code character varying; - ---trade_bloc-- -CREATE TABLE nw.trade_bloc -( - id BIGSERIAL NOT NULL, - version_id BIGINT NOT NULL, - trade_bloc_ref_id CHARACTER VARYING NOT NULL, - name CHARACTER VARYING NOT NULL, - description CHARACTER VARYING NOT NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT trade_bloc_pkey PRIMARY KEY (id) -); - ---country-- -CREATE TABLE nw.country -( - id BIGSERIAL NOT NULL, - version_id BIGINT NOT NULL, - country_ref_id CHARACTER VARYING NOT NULL, - name CHARACTER VARYING NOT NULL, - trade_bloc TEXT[] NOT NULL, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - CONSTRAINT country_pkey PRIMARY KEY (id) -); diff --git a/src/main/resources/db/migration/V85__fix_not_req_field.sql b/src/main/resources/db/migration/V85__fix_not_req_field.sql deleted file mode 100644 index 368dfe5f..00000000 --- a/src/main/resources/db/migration/V85__fix_not_req_field.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE nw.trade_bloc - ALTER COLUMN description DROP NOT NULL; diff --git a/src/main/resources/db/migration/V86__destination_type_digital_wallet.sql b/src/main/resources/db/migration/V86__destination_type_digital_wallet.sql deleted file mode 100644 index 90b73713..00000000 --- a/src/main/resources/db/migration/V86__destination_type_digital_wallet.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TYPE nw.destination_resource_type ADD VALUE 'digital_wallet' AFTER 'crypto_wallet'; diff --git a/src/main/resources/db/migration/V87__destination_digital_wallet.sql b/src/main/resources/db/migration/V87__destination_digital_wallet.sql deleted file mode 100644 index 003f956a..00000000 --- a/src/main/resources/db/migration/V87__destination_digital_wallet.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE nw.destination ADD COLUMN resource_digital_wallet_id CHARACTER VARYING; -ALTER TABLE nw.destination ADD COLUMN resource_digital_wallet_data CHARACTER VARYING; - -ALTER TABLE nw.withdrawal_session ADD COLUMN resource_digital_wallet_id CHARACTER VARYING; -ALTER TABLE nw.withdrawal_session ADD COLUMN resource_digital_wallet_data CHARACTER VARYING; diff --git a/src/main/resources/db/migration/V88__new_payouts.sql b/src/main/resources/db/migration/V88__new_payouts.sql deleted file mode 100644 index f9abd076..00000000 --- a/src/main/resources/db/migration/V88__new_payouts.sql +++ /dev/null @@ -1,23 +0,0 @@ -DROP TABLE nw.payout cascade; -DROP TABLE nw.payout_summary cascade; - -create table if not exists nw.payout -( - id bigserial not null, - payout_id varchar not null, - event_created_at timestamp not null, - sequence_id int not null, - created_at timestamp not null, - party_id varchar not null, - shop_id varchar not null, - status nw.payout_status not null, - payout_tool_id varchar not null, - amount bigint not null, - fee bigint default 0 not null, - currency_code varchar not null, - cancelled_details varchar, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), - current BOOLEAN NOT NULL DEFAULT TRUE, - constraint payout_id_pkey primary key (id), - constraint payout_payout_id_ukey unique (payout_id, sequence_id) - ); \ No newline at end of file diff --git a/src/main/resources/db/migration/V89__add_payout_tool_info_type.sql b/src/main/resources/db/migration/V89__add_payout_tool_info_type.sql deleted file mode 100644 index 458d108b..00000000 --- a/src/main/resources/db/migration/V89__add_payout_tool_info_type.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TYPE nw.payout_tool_info ADD VALUE 'payment_institution_account'; \ No newline at end of file diff --git a/src/main/resources/db/migration/V8__session_change.sql b/src/main/resources/db/migration/V8__session_change.sql deleted file mode 100644 index 149c77a2..00000000 --- a/src/main/resources/db/migration/V8__session_change.sql +++ /dev/null @@ -1,25 +0,0 @@ -CREATE TYPE nw.session_target_status AS ENUM('processed', 'captured', 'cancelled', 'refunded'); -CREATE TYPE nw.session_change_payload AS ENUM('session_started', 'session_finished', 'session_suspended', 'session_activated', 'session_transaction_bound', 'session_proxy_state_changed', 'session_interaction_requested'); -CREATE TYPE nw.session_change_payload_finished_result AS ENUM('succeeded', 'failed'); - -ALTER TABLE nw.payment ADD COLUMN session_target nw.session_target_status; -ALTER TABLE nw.payment ADD COLUMN session_payload nw.session_change_payload; -ALTER TABLE nw.payment ADD COLUMN session_payload_finished_result nw.session_change_payload_finished_result; -ALTER TABLE nw.payment ADD COLUMN session_payload_finished_result_failed_failure_json CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN session_payload_suspended_tag CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN session_payload_transaction_bound_trx_id CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN session_payload_transaction_bound_trx_timestamp TIMESTAMP WITHOUT TIME ZONE; -ALTER TABLE nw.payment ADD COLUMN session_payload_transaction_bound_trx_extra_json CHARACTER VARYING; -ALTER TABLE nw.payment ADD COLUMN session_payload_proxy_state_changed_proxy_state BYTEA; -ALTER TABLE nw.payment ADD COLUMN session_payload_interaction_requested_interaction_json CHARACTER VARYING; - -ALTER TABLE nw.refund ADD COLUMN session_target nw.session_target_status; -ALTER TABLE nw.refund ADD COLUMN session_payload nw.session_change_payload; -ALTER TABLE nw.refund ADD COLUMN session_payload_finished_result nw.session_change_payload_finished_result; -ALTER TABLE nw.refund ADD COLUMN session_payload_finished_result_failed_failure_json CHARACTER VARYING; -ALTER TABLE nw.refund ADD COLUMN session_payload_suspended_tag CHARACTER VARYING; -ALTER TABLE nw.refund ADD COLUMN session_payload_transaction_bound_trx_id CHARACTER VARYING; -ALTER TABLE nw.refund ADD COLUMN session_payload_transaction_bound_trx_timestamp TIMESTAMP WITHOUT TIME ZONE; -ALTER TABLE nw.refund ADD COLUMN session_payload_transaction_bound_trx_extra_json CHARACTER VARYING; -ALTER TABLE nw.refund ADD COLUMN session_payload_proxy_state_changed_proxy_state BYTEA; -ALTER TABLE nw.refund ADD COLUMN session_payload_interaction_requested_interaction_json CHARACTER VARYING; \ No newline at end of file diff --git a/src/main/resources/db/migration/V90__drop_non_null_provider_json_field.sql b/src/main/resources/db/migration/V90__drop_non_null_provider_json_field.sql deleted file mode 100644 index e89e8962..00000000 --- a/src/main/resources/db/migration/V90__drop_non_null_provider_json_field.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE nw.payment_institution ALTER COLUMN providers_json DROP NOT NULL; diff --git a/src/main/resources/db/migration/V91__drop_cls_field_identity.sql b/src/main/resources/db/migration/V91__drop_cls_field_identity.sql deleted file mode 100644 index 0cfa10ad..00000000 --- a/src/main/resources/db/migration/V91__drop_cls_field_identity.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE nw.identity -DROP COLUMN IF EXISTS identity_class_id; diff --git a/src/main/resources/db/migration/V92__add_schedlock.sql b/src/main/resources/db/migration/V92__add_schedlock.sql deleted file mode 100644 index e0ff0d18..00000000 --- a/src/main/resources/db/migration/V92__add_schedlock.sql +++ /dev/null @@ -1,8 +0,0 @@ -CREATE TABLE nw.shedlock -( - name VARCHAR(64), - lock_until TIMESTAMP(3) NULL, - locked_at TIMESTAMP(3) NULL, - locked_by VARCHAR(255), - PRIMARY KEY (name) -); \ No newline at end of file diff --git a/src/main/resources/db/migration/V93__add_adjustmen_diffs.sql b/src/main/resources/db/migration/V93__add_adjustmen_diffs.sql deleted file mode 100644 index 3a05c756..00000000 --- a/src/main/resources/db/migration/V93__add_adjustmen_diffs.sql +++ /dev/null @@ -1,4 +0,0 @@ -ALTER TABLE nw.adjustment ADD COLUMN IF NOT EXISTS provider_amount_diff BIGINT DEFAULT 0; -ALTER TABLE nw.adjustment ADD COLUMN IF NOT EXISTS system_amount_diff BIGINT DEFAULT 0; -ALTER TABLE nw.adjustment ADD COLUMN IF NOT EXISTS external_income_amount_diff BIGINT DEFAULT 0; -ALTER TABLE nw.adjustment ADD COLUMN IF NOT EXISTS external_outcome_amount_diff BIGINT DEFAULT 0; \ No newline at end of file diff --git a/src/main/resources/db/migration/V94__add_generic_type.sql b/src/main/resources/db/migration/V94__add_generic_type.sql deleted file mode 100644 index e43af50b..00000000 --- a/src/main/resources/db/migration/V94__add_generic_type.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TYPE nw.payment_method_type ADD VALUE 'generic'; -ALTER TYPE nw.destination_resource_type ADD VALUE 'generic'; diff --git a/src/main/resources/db/migration/V9__truncate_party_mngmnt_for_contractors.sql b/src/main/resources/db/migration/V9__truncate_party_mngmnt_for_contractors.sql deleted file mode 100644 index a7110b19..00000000 --- a/src/main/resources/db/migration/V9__truncate_party_mngmnt_for_contractors.sql +++ /dev/null @@ -1 +0,0 @@ -TRUNCATE nw.party, nw.contract, nw.contractor, nw.shop CASCADE; \ No newline at end of file diff --git a/src/test/java/dev/vality/newway/IntegrationTest.java b/src/test/java/dev/vality/newway/IntegrationTest.java index 9c6ffe9c..774128c3 100644 --- a/src/test/java/dev/vality/newway/IntegrationTest.java +++ b/src/test/java/dev/vality/newway/IntegrationTest.java @@ -1,26 +1,39 @@ package dev.vality.newway; import dev.vality.damsel.domain.*; +import dev.vality.damsel.domain.PaymentRoute; import dev.vality.damsel.payment_processing.*; import dev.vality.geck.common.util.TypeUtil; import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.machinegun.msgpack.Value; import dev.vality.newway.config.PostgresqlSpringBootITest; -import dev.vality.newway.dao.invoicing.iface.PaymentDao; +import dev.vality.newway.dao.invoicing.iface.*; +import dev.vality.newway.domain.enums.PayerType; +import dev.vality.newway.domain.enums.PaymentChangeType; import dev.vality.newway.domain.enums.PaymentStatus; -import dev.vality.newway.domain.tables.pojos.Payment; +import dev.vality.newway.domain.enums.PaymentToolType; +import dev.vality.newway.domain.tables.pojos.*; +import dev.vality.newway.domain.tables.pojos.Invoice; +import dev.vality.newway.domain.tables.pojos.InvoiceCart; import dev.vality.newway.service.InvoicingService; import dev.vality.newway.utils.MockUtils; import dev.vality.sink.common.serialization.impl.PaymentEventPayloadSerializer; -import org.junit.jupiter.api.Assertions; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.List; -import java.util.Objects; +import java.util.Map; +import static dev.vality.newway.utils.JdbcUtil.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +@Slf4j @PostgresqlSpringBootITest public class IntegrationTest { @@ -30,18 +43,202 @@ public class IntegrationTest { @Autowired private JdbcTemplate jdbcTemplate; + @Autowired + private InvoiceDao invoiceDao; + @Autowired + private InvoiceStatusInfoDao invoiceStatusInfoDao; + @Autowired + private InvoiceCartDao invoiceCartDao; @Autowired private PaymentDao paymentDao; + @Autowired + private PaymentStatusInfoDao paymentStatusInfoDao; + @Autowired + private PaymentPayerInfoDao paymentPayerInfoDao; + @Autowired + private PaymentAdditionalInfoDao paymentAdditionalInfoDao; + @Autowired + private PaymentRecurrentInfoDao paymentRecurrentInfoDao; + @Autowired + private PaymentRiskDataDao paymentRiskDataDao; + @Autowired + private PaymentFeeDao paymentFeeDao; + @Autowired + private PaymentRouteDao paymentRouteDao; + @Autowired + private CashFlowLinkDao cashFlowLinkDao; + @Autowired + private CashFlowDao cashFlowDao; + + private final PaymentEventPayloadSerializer serializer = new PaymentEventPayloadSerializer(); + private final String invoiceId = "invoiceId"; + private final String paymentId = "paymentId"; + private final String partyId = "party_1"; + private final String shopId = "shop_id"; @Test public void test() { - PaymentEventPayloadSerializer serializer = new PaymentEventPayloadSerializer(); - String invoiceId = "inv_id"; - String paymentId = "1"; - List machineEventsFirst = List.of( + cleanUpTables(); + + List machineEventsFirst = getInitialInvoicePaymentEvents(invoiceId, paymentId); + log.info("Processing first batch of machine events"); + invoicingService.handleEvents(machineEventsFirst); + + Invoice invoice = invoiceDao.get(invoiceId); + assertEquals(partyId, invoice.getPartyId()); + assertEquals(shopId, invoice.getShopId()); + assertEquals(1, invoice.getAmount()); + assertEquals("RUB", invoice.getCurrencyCode()); + assertEquals(1, invoice.getSequenceId()); + assertEquals(0, invoice.getChangeId()); + + List invoiceCarts = invoiceCartDao.getByInvoiceId(invoiceId); + assertEquals(1, invoiceCarts.size()); + InvoiceCart cart = invoiceCarts.get(0); + assertEquals(1, cart.getQuantity()); + assertEquals(12, cart.getAmount()); + assertEquals("RUB", cart.getCurrencyCode()); + assertEquals("{}", cart.getMetadataJson()); + assertEquals(1, cart.getSequenceId()); + assertEquals(0, cart.getChangeId()); + + InvoiceStatusInfo invoiceStatusInfo = invoiceStatusInfoDao.get(invoiceId); + assertEquals(dev.vality.newway.domain.enums.InvoiceStatus.fulfilled, invoiceStatusInfo.getStatus()); + assertEquals("keks", invoiceStatusInfo.getDetails()); + assertEquals(1, invoiceStatusInfo.getSequenceId()); + assertEquals(1, invoiceStatusInfo.getChangeId()); + + Payment payment = paymentDao.get(invoiceId, paymentId); + assertEquals(partyId, payment.getPartyId()); + assertEquals(shopId, payment.getShopId()); + assertEquals(11, payment.getAmount()); + assertEquals("RUB", payment.getCurrencyCode()); + assertEquals(1, payment.getSequenceId()); + assertEquals(2, payment.getChangeId()); + + PaymentPayerInfo paymentPayerInfo = paymentPayerInfoDao.get(invoiceId, paymentId); + assertEquals(PaymentToolType.payment_terminal, paymentPayerInfo.getPaymentToolType()); + assertEquals(PayerType.recurrent, paymentPayerInfo.getPayerType()); + assertEquals("1", paymentPayerInfo.getRecurrentParentInvoiceId()); + assertEquals("2", paymentPayerInfo.getRecurrentParentPaymentId()); + assertEquals(1, paymentPayerInfo.getSequenceId()); + assertEquals(2, paymentPayerInfo.getChangeId()); + + var paymentRoute = paymentRouteDao.get(invoiceId, paymentId); + assertEquals(29, paymentRoute.getRouteProviderId()); + assertEquals(30, paymentRoute.getRouteTerminalId()); + assertEquals(1, paymentRoute.getSequenceId()); + assertEquals(2, paymentRoute.getChangeId()); + + CashFlowLink cashFlowLink = cashFlowLinkDao.get(invoiceId, paymentId); + assertEquals(1, cashFlowLink.getSequenceId()); + assertEquals(3, cashFlowLink.getChangeId()); + + List cashFlows = cashFlowDao.getByObjId(cashFlowLink.getId(), PaymentChangeType.payment); + assertEquals(1, cashFlows.size()); + CashFlow cashFlow = cashFlows.get(0); + assertEquals(1, cashFlow.getAmount()); + assertEquals("RUB", cashFlow.getCurrencyCode()); + assertEquals(1, cashFlow.getSourceAccountId()); + assertEquals(SystemCashFlowAccount.settlement.name(), cashFlow.getSourceAccountTypeValue()); + assertEquals(1, cashFlow.getDestinationAccountId()); + assertEquals(SystemCashFlowAccount.settlement.name(), cashFlow.getDestinationAccountTypeValue()); + + PaymentFee paymentFee = paymentFeeDao.get(invoiceId, paymentId); + assertEquals(0, paymentFee.getFee()); + assertEquals(0, paymentFee.getExternalFee()); + assertEquals(0, paymentFee.getProviderFee()); + assertEquals(0, paymentFee.getGuaranteeDeposit()); + assertEquals(1, paymentFee.getSequenceId()); + assertEquals(3, paymentFee.getChangeId()); + + PaymentRiskData paymentRiskData = paymentRiskDataDao.get(invoiceId, paymentId); + assertEquals("high", paymentRiskData.getRiskScore().name()); + assertEquals(1, paymentRiskData.getSequenceId()); + assertEquals(4, paymentRiskData.getChangeId()); + + PaymentStatusInfo statusInfo = paymentStatusInfoDao.get(invoiceId, paymentId); + assertEquals(PaymentStatus.captured, statusInfo.getStatus()); + assertEquals(1, statusInfo.getSequenceId()); + assertEquals(5, statusInfo.getChangeId()); + + PaymentAdditionalInfo paymentAdditionalInfo = paymentAdditionalInfoDao.get(invoiceId, paymentId); + assertEquals("trxId", paymentAdditionalInfo.getTransactionId()); + assertEquals("{\"lol\":\"kek\"}", paymentAdditionalInfo.getExtraJson()); + assertEquals(1, paymentAdditionalInfo.getSequenceId()); + assertEquals(6, paymentAdditionalInfo.getChangeId()); + + //--- second changes - only update + List machineEventsSecond = getInvoicePaymentChanges(invoiceId, paymentId); + log.info("Processing second batch of machine events"); + invoicingService.handleEvents(machineEventsSecond); + + PaymentRiskData paymentRiskDataSecond = paymentRiskDataDao.get(invoiceId, paymentId); + assertEquals("low", paymentRiskDataSecond.getRiskScore().name()); + assertEquals(2, paymentRiskDataSecond.getSequenceId()); + assertEquals(0, paymentRiskDataSecond.getChangeId()); + + PaymentRecurrentInfo recurrentInfoSecond = paymentRecurrentInfoDao.get(invoiceId, paymentId); + assertEquals("keks", recurrentInfoSecond.getToken()); + assertEquals(2, recurrentInfoSecond.getSequenceId()); + assertEquals(1, recurrentInfoSecond.getChangeId()); + + var paymentRouteSecond = paymentRouteDao.get(invoiceId, paymentId); + assertEquals(31, paymentRouteSecond.getRouteProviderId()); + assertEquals(32, paymentRouteSecond.getRouteTerminalId()); + assertEquals(2, paymentRouteSecond.getSequenceId()); + assertEquals(2, paymentRouteSecond.getChangeId()); + + CashFlowLink cashFlowLinkSecond = cashFlowLinkDao.get(invoiceId, paymentId); + assertNotEquals(cashFlowLinkSecond.getId(), cashFlowLink.getId()); + assertEquals(2, cashFlowLinkSecond.getSequenceId()); + assertEquals(3, cashFlowLinkSecond.getChangeId()); + + List secondCashFlows = cashFlowDao.getByObjId(cashFlowLinkSecond.getId(), PaymentChangeType.payment); + assertEquals(1, secondCashFlows.size()); + CashFlow cashFlowSecond = secondCashFlows.get(0); + assertEquals(1, cashFlowSecond.getAmount()); + assertEquals("RUB", cashFlowSecond.getCurrencyCode()); + assertEquals(5, cashFlowSecond.getSourceAccountId()); + assertEquals(MerchantCashFlowAccount.settlement.name(), cashFlowSecond.getSourceAccountTypeValue()); + assertEquals(1, cashFlowSecond.getDestinationAccountId()); + assertEquals(SystemCashFlowAccount.settlement.name(), cashFlowSecond.getDestinationAccountTypeValue()); + + PaymentFee paymentFeeSecond = paymentFeeDao.get(invoiceId, paymentId); + assertEquals(1, paymentFeeSecond.getFee()); + assertEquals(0, paymentFeeSecond.getExternalFee()); + assertEquals(0, paymentFeeSecond.getProviderFee()); + assertEquals(0, paymentFeeSecond.getGuaranteeDeposit()); + assertEquals(2, paymentFeeSecond.getSequenceId()); + assertEquals(3, paymentFeeSecond.getChangeId()); + + //--- third changes - insert + List machineEventsThird = getInvoicePaymentFailedChange(invoiceId, paymentId); + log.info("Processing third batch of machine events"); + invoicingService.handleEvents(machineEventsThird); + + PaymentStatusInfo statusInfoThird = paymentStatusInfoDao.get(invoiceId, paymentId); + assertEquals(PaymentStatus.failed, statusInfoThird.getStatus()); + assertEquals("{\"failure\":{\"operation_timeout\":{}}}", statusInfoThird.getReason()); + assertEquals(3, statusInfoThird.getSequenceId()); + assertEquals(0, statusInfoThird.getChangeId()); + + //--- duplication check + log.info("Duplication check first batch"); + invoicingService.handleEvents(machineEventsFirst); + log.info("Duplication check second batch"); + invoicingService.handleEvents(machineEventsSecond); + log.info("Duplication check third batch"); + invoicingService.handleEvents(machineEventsThird); + assertDuplication(); + } + + @NotNull + private List getInitialInvoicePaymentEvents(String invoiceId, String paymentId) { + return List.of( new MachineEvent().setSourceId(invoiceId) .setEventId(1) - .setCreatedAt(TypeUtil.temporalToString(LocalDateTime.now())) + .setCreatedAt(TypeUtil.temporalToString(LocalDateTime.now().truncatedTo(ChronoUnit.MICROS))) .setData(Value.bin(serializer.serialize( EventPayload.invoice_changes( List.of(InvoiceChange.invoice_created(new InvoiceCreated() @@ -55,7 +252,25 @@ public void test() { .setPayload(InvoicePaymentChangePayload .invoice_payment_started(new InvoicePaymentStarted() .setPayment( - MockUtils.buildPayment(paymentId))))), + MockUtils.buildPayment(paymentId)) + .setCashFlow(List.of(new FinalCashFlowPosting() + .setSource( + new FinalCashFlowAccount( + CashFlowAccount + .system(SystemCashFlowAccount.settlement), + 1)) + .setDestination( + new FinalCashFlowAccount( + CashFlowAccount + .system(SystemCashFlowAccount.settlement), + 1)) + .setVolume(new Cash(2, + new CurrencyRef( + "RUB"))))) + .setRoute(new PaymentRoute( + new ProviderRef(29), + new TerminalRef(30))) + ))), InvoiceChange.invoice_payment_change(new InvoicePaymentChange() .setId(paymentId) .setPayload(InvoicePaymentChangePayload @@ -89,25 +304,28 @@ public void test() { new InvoicePaymentStatusChanged().setStatus( InvoicePaymentStatus.captured( new InvoicePaymentCaptured())) - )))))))) - ); - invoicingService.handleEvents(machineEventsFirst); - Assertions.assertEquals(3, Objects.requireNonNull(jdbcTemplate.queryForObject("SELECT count(*) FROM nw.payment " + - "WHERE invoice_id = ? and payment_id = ? ", - new Object[]{invoiceId, paymentId}, Integer.class)).intValue()); - - Payment payment = paymentDao.get(invoiceId, paymentId); - Assertions.assertEquals(PaymentStatus.captured, payment.getStatus()); - Assertions.assertEquals("high", payment.getRiskScore().name()); - Assertions.assertEquals(1, Objects.requireNonNull(jdbcTemplate.queryForObject("SELECT count(*) FROM nw.cash_flow WHERE obj_id = ? ", - new Object[]{payment.getId()}, Integer.class)).intValue()); - - //--- second changes - only update + ))), + InvoiceChange.invoice_payment_change(new InvoicePaymentChange() + .setId(paymentId) + .setPayload(InvoicePaymentChangePayload + .invoice_payment_session_change( + new InvoicePaymentSessionChange() + .setTarget(TargetInvoicePaymentStatus.processed(new InvoicePaymentProcessed())) + .setPayload(SessionChangePayload.session_transaction_bound( + new SessionTransactionBound() + .setTrx(new TransactionInfo() + .setId("trxId") + .setExtra(Map.of("lol", "kek")) + )))))) + )))))); + } - List machineEventsSecond = List.of( + @NotNull + private List getInvoicePaymentChanges(String invoiceId, String paymentId) { + return List.of( new MachineEvent().setSourceId(invoiceId) .setEventId(2) - .setCreatedAt(TypeUtil.temporalToString(LocalDateTime.now())) + .setCreatedAt(TypeUtil.temporalToString(LocalDateTime.now().truncatedTo(ChronoUnit.MICROS))) .setData(Value.bin(serializer.serialize( EventPayload.invoice_changes( List.of(InvoiceChange.invoice_payment_change(new InvoicePaymentChange() @@ -122,27 +340,46 @@ public void test() { .setPayload(InvoicePaymentChangePayload .invoice_payment_rec_token_acquired( new InvoicePaymentRecTokenAcquired("keks") + ))), + InvoiceChange.invoice_payment_change(new InvoicePaymentChange() + .setId(paymentId) + .setPayload(InvoicePaymentChangePayload + .invoice_payment_route_changed( + new InvoicePaymentRouteChanged() + .setRoute(new PaymentRoute( + new ProviderRef(31), + new TerminalRef(32))) + ))), + InvoiceChange.invoice_payment_change(new InvoicePaymentChange() + .setId(paymentId) + .setPayload(InvoicePaymentChangePayload + .invoice_payment_cash_flow_changed( + new InvoicePaymentCashFlowChanged( + List.of(new FinalCashFlowPosting() + .setSource( + new FinalCashFlowAccount( + CashFlowAccount + .merchant(MerchantCashFlowAccount.settlement), + 5)) + .setDestination( + new FinalCashFlowAccount( + CashFlowAccount + .system(SystemCashFlowAccount.settlement), + 1)) + .setVolume(new Cash(1, + new CurrencyRef( + "RUB"))))) ))) ))))) ); + } - invoicingService.handleEvents(machineEventsSecond); - - Assertions.assertEquals(3, Objects.requireNonNull(jdbcTemplate.queryForObject("SELECT count(*) FROM nw.payment " + - "WHERE invoice_id = ? and payment_id = ? ", - new Object[]{invoiceId, paymentId}, Integer.class)).intValue()); - - Payment paymentSecond = paymentDao.get(invoiceId, paymentId); - Assertions.assertEquals("low", paymentSecond.getRiskScore().name()); - Assertions.assertEquals("keks", paymentSecond.getRecurrentIntentionToken()); - Assertions.assertEquals(paymentSecond.getId(), payment.getId()); - - //--- third changes - insert - - List machineEventsThird = List.of( + @NotNull + private List getInvoicePaymentFailedChange(String invoiceId, String paymentId) { + return List.of( new MachineEvent().setSourceId(invoiceId) .setEventId(3) - .setCreatedAt(TypeUtil.temporalToString(LocalDateTime.now())) + .setCreatedAt(TypeUtil.temporalToString(LocalDateTime.now().truncatedTo(ChronoUnit.MICROS))) .setData(Value.bin(serializer.serialize( EventPayload.invoice_changes( List.of(InvoiceChange.invoice_payment_change(new InvoicePaymentChange() @@ -155,25 +392,45 @@ public void test() { new OperationTimeout())))) )))))))) ); + } - invoicingService.handleEvents(machineEventsThird); - - Assertions.assertEquals(4, Objects.requireNonNull(jdbcTemplate.queryForObject("SELECT count(*) FROM nw.payment " + - "WHERE invoice_id = ? and payment_id = ? ", - new Object[]{invoiceId, paymentId}, Integer.class)).intValue()); - - Payment paymentThird = paymentDao.get(invoiceId, paymentId); - Assertions.assertEquals(PaymentStatus.failed, paymentThird.getStatus()); - Assertions.assertEquals(3, paymentThird.getSequenceId().longValue()); - Assertions.assertNotEquals(paymentSecond.getId(), paymentThird.getId()); - - Assertions.assertEquals(1, Objects.requireNonNull(jdbcTemplate.queryForObject("SELECT count(*) FROM nw.cash_flow WHERE obj_id = ? ", - new Object[]{paymentThird.getId()}, Integer.class)).intValue()); + private void cleanUpTables() { + jdbcTemplate.execute("truncate table dw.invoice cascade"); + jdbcTemplate.execute("truncate table dw.invoice_status_info cascade"); + jdbcTemplate.execute("truncate table dw.invoice_cart cascade"); + jdbcTemplate.execute("truncate table dw.payment cascade"); + jdbcTemplate.execute("truncate table dw.payment_status_info cascade"); + jdbcTemplate.execute("truncate table dw.payment_payer_info cascade"); + jdbcTemplate.execute("truncate table dw.payment_additional_info cascade"); + jdbcTemplate.execute("truncate table dw.payment_recurrent_info cascade"); + jdbcTemplate.execute("truncate table dw.payment_risk_data cascade"); + jdbcTemplate.execute("truncate table dw.payment_fee cascade"); + jdbcTemplate.execute("truncate table dw.payment_route cascade"); + jdbcTemplate.execute("truncate table dw.cash_flow_link cascade"); + jdbcTemplate.execute("truncate table dw.cash_flow cascade"); + } - //--- duplication check + private void assertDuplication() { + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment", invoiceId, paymentId, false)); + assertEquals(3, countPaymentEntity(jdbcTemplate, "payment_status_info", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_status_info", invoiceId, paymentId, true)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_payer_info", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_additional_info", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_additional_info", invoiceId, paymentId, true)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_recurrent_info", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_recurrent_info", invoiceId, paymentId, true)); + assertEquals(2, countPaymentEntity(jdbcTemplate, "payment_risk_data", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_risk_data", invoiceId, paymentId, true)); + assertEquals(3, countPaymentEntity(jdbcTemplate, "payment_fee", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_fee", invoiceId, paymentId, true)); + assertEquals(2, countPaymentEntity(jdbcTemplate, "payment_route", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_route", invoiceId, paymentId, true)); + assertEquals(3, countPaymentEntity(jdbcTemplate, "cash_flow_link", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "cash_flow_link", invoiceId, paymentId, true)); + assertEquals(3, countEntities(jdbcTemplate, "cash_flow")); - invoicingService.handleEvents(machineEventsFirst); - Assertions.assertEquals(4, Objects.requireNonNull(jdbcTemplate.queryForObject("SELECT count(*) FROM nw.payment WHERE invoice_id = ? and payment_id = ? ", - new Object[]{invoiceId, paymentId}, Integer.class)).intValue()); + assertEquals(2, countInvoiceEntity(jdbcTemplate, "invoice_status_info", invoiceId, false)); + assertEquals(1, countInvoiceEntity(jdbcTemplate, "invoice_status_info", invoiceId, true)); + assertEquals(1, countInvoiceEntity(jdbcTemplate, "invoice_cart", invoiceId, false)); } } diff --git a/src/test/java/dev/vality/newway/dao/ChallengeDaoTest.java b/src/test/java/dev/vality/newway/dao/ChallengeDaoTest.java index 86477cde..2708fcd2 100644 --- a/src/test/java/dev/vality/newway/dao/ChallengeDaoTest.java +++ b/src/test/java/dev/vality/newway/dao/ChallengeDaoTest.java @@ -19,7 +19,7 @@ public class ChallengeDaoTest { @Test public void challengeDaoTest() { - jdbcTemplate.execute("truncate table nw.challenge cascade"); + jdbcTemplate.execute("truncate table dw.challenge cascade"); Challenge challenge = dev.vality.testcontainers.annotations.util.RandomBeans.random(Challenge.class); challenge.setCurrent(true); Long id = challengeDao.save(challenge).get(); diff --git a/src/test/java/dev/vality/newway/dao/DaoTests.java b/src/test/java/dev/vality/newway/dao/DaoTests.java index 5ec5779f..9ddf8b4b 100644 --- a/src/test/java/dev/vality/newway/dao/DaoTests.java +++ b/src/test/java/dev/vality/newway/dao/DaoTests.java @@ -3,17 +3,13 @@ import dev.vality.newway.config.PostgresqlSpringBootITest; import dev.vality.newway.dao.dominant.iface.DominantDao; import dev.vality.newway.dao.dominant.impl.*; -import dev.vality.newway.dao.invoicing.iface.AdjustmentDao; -import dev.vality.newway.dao.invoicing.iface.CashFlowDao; -import dev.vality.newway.dao.invoicing.iface.InvoiceCartDao; -import dev.vality.newway.dao.invoicing.iface.RefundDao; +import dev.vality.newway.dao.invoicing.iface.*; +import dev.vality.newway.dao.invoicing.impl.CashFlowLinkIdsGeneratorDaoImpl; import dev.vality.newway.dao.invoicing.impl.InvoiceDaoImpl; import dev.vality.newway.dao.invoicing.impl.PaymentDaoImpl; -import dev.vality.newway.dao.invoicing.impl.PaymentIdsGeneratorDaoImpl; import dev.vality.newway.dao.party.iface.*; import dev.vality.newway.dao.rate.iface.RateDao; import dev.vality.newway.dao.recurrent.payment.tool.iface.RecurrentPaymentToolDao; -import dev.vality.newway.domain.enums.AdjustmentCashFlowType; import dev.vality.newway.domain.enums.CashFlowAccount; import dev.vality.newway.domain.enums.PaymentChangeType; import dev.vality.newway.domain.tables.pojos.Calendar; @@ -21,20 +17,21 @@ import dev.vality.newway.domain.tables.pojos.*; import dev.vality.newway.exception.NotFoundException; import dev.vality.newway.model.InvoicingKey; -import dev.vality.newway.model.InvoicingType; import dev.vality.newway.utils.HashUtil; +import dev.vality.testcontainers.annotations.util.RandomBeans; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.SingleColumnRowMapper; import java.util.*; import java.util.stream.LongStream; import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; @PostgresqlSpringBootITest public class DaoTests { @@ -68,16 +65,34 @@ public class DaoTests { @Autowired private DominantDao dominantDao; @Autowired + private CashFlowLinkDao cashFlowLinkDao; + @Autowired private CashFlowDao cashFlowDao; @Autowired private AdjustmentDao adjustmentDao; @Autowired private PaymentDaoImpl paymentDao; @Autowired + private PaymentStatusInfoDao paymentStatusInfoDao; + @Autowired + private PaymentPayerInfoDao paymentPayerInfoDao; + @Autowired + private PaymentAdditionalInfoDao paymentAdditionalInfoDao; + @Autowired + private PaymentRecurrentInfoDao paymentRecurrentInfoDao; + @Autowired + private PaymentRiskDataDao paymentRiskDataDao; + @Autowired + private PaymentFeeDao paymentFeeDao; + @Autowired + private PaymentRouteDao paymentRouteDao; + @Autowired private InvoiceCartDao invoiceCartDao; @Autowired private InvoiceDaoImpl invoiceDao; @Autowired + private InvoiceStatusInfoDao invoiceStatusInfoDao; + @Autowired private RefundDao refundDao; @Autowired private ContractAdjustmentDao contractAdjustmentDao; @@ -94,82 +109,82 @@ public class DaoTests { @Autowired private RateDao rateDao; @Autowired - private PaymentIdsGeneratorDaoImpl idsGeneratorDao; + private CashFlowLinkIdsGeneratorDaoImpl idsGeneratorDao; @Autowired private RecurrentPaymentToolDao recurrentPaymentToolDao; @Test public void dominantDaoTest() { - jdbcTemplate.execute("truncate table nw.calendar cascade"); - jdbcTemplate.execute("truncate table nw.category cascade"); - jdbcTemplate.execute("truncate table nw.currency cascade"); - jdbcTemplate.execute("truncate table nw.inspector cascade"); - jdbcTemplate.execute("truncate table nw.payment_institution cascade"); - jdbcTemplate.execute("truncate table nw.payment_method cascade"); - jdbcTemplate.execute("truncate table nw.payout_method cascade"); - jdbcTemplate.execute("truncate table nw.provider cascade"); - jdbcTemplate.execute("truncate table nw.withdrawal_provider cascade"); - jdbcTemplate.execute("truncate table nw.proxy cascade"); - jdbcTemplate.execute("truncate table nw.terminal cascade"); - jdbcTemplate.execute("truncate table nw.term_set_hierarchy cascade"); - - var calendar = dev.vality.testcontainers.annotations.util.RandomBeans.random(Calendar.class); + jdbcTemplate.execute("truncate table dw.calendar cascade"); + jdbcTemplate.execute("truncate table dw.category cascade"); + jdbcTemplate.execute("truncate table dw.currency cascade"); + jdbcTemplate.execute("truncate table dw.inspector cascade"); + jdbcTemplate.execute("truncate table dw.payment_institution cascade"); + jdbcTemplate.execute("truncate table dw.payment_method cascade"); + jdbcTemplate.execute("truncate table dw.payout_method cascade"); + jdbcTemplate.execute("truncate table dw.provider cascade"); + jdbcTemplate.execute("truncate table dw.withdrawal_provider cascade"); + jdbcTemplate.execute("truncate table dw.proxy cascade"); + jdbcTemplate.execute("truncate table dw.terminal cascade"); + jdbcTemplate.execute("truncate table dw.term_set_hierarchy cascade"); + + var calendar = RandomBeans.random(Calendar.class); calendar.setCurrent(true); calendarDao.save(calendar); calendarDao.updateNotCurrent(calendar.getCalendarRefId()); - Category category = dev.vality.testcontainers.annotations.util.RandomBeans.random(Category.class); + Category category = RandomBeans.random(Category.class); category.setCurrent(true); categoryDao.save(category); categoryDao.updateNotCurrent(category.getCategoryRefId()); - var currency = dev.vality.testcontainers.annotations.util.RandomBeans.random(Currency.class); + var currency = RandomBeans.random(Currency.class); currency.setCurrent(true); currencyDao.save(currency); currencyDao.updateNotCurrent(currency.getCurrencyRefId()); - Inspector inspector = dev.vality.testcontainers.annotations.util.RandomBeans.random(Inspector.class); + Inspector inspector = RandomBeans.random(Inspector.class); inspector.setCurrent(true); inspectorDao.save(inspector); inspectorDao.updateNotCurrent(inspector.getInspectorRefId()); - PaymentInstitution paymentInstitution = dev.vality.testcontainers.annotations.util.RandomBeans.random(PaymentInstitution.class); + PaymentInstitution paymentInstitution = RandomBeans.random(PaymentInstitution.class); paymentInstitution.setCurrent(true); paymentInstitutionDao.save(paymentInstitution); paymentInstitutionDao.updateNotCurrent(paymentInstitution.getPaymentInstitutionRefId()); - PaymentMethod paymentMethod = dev.vality.testcontainers.annotations.util.RandomBeans.random(PaymentMethod.class); + PaymentMethod paymentMethod = RandomBeans.random(PaymentMethod.class); paymentMethod.setCurrent(true); paymentMethodDao.save(paymentMethod); paymentMethodDao.updateNotCurrent(paymentMethod.getPaymentMethodRefId()); - PayoutMethod payoutMethod = dev.vality.testcontainers.annotations.util.RandomBeans.random(PayoutMethod.class); + PayoutMethod payoutMethod = RandomBeans.random(PayoutMethod.class); payoutMethod.setCurrent(true); payoutMethodDao.save(payoutMethod); payoutMethodDao.updateNotCurrent(payoutMethod.getPayoutMethodRefId()); - Provider provider = dev.vality.testcontainers.annotations.util.RandomBeans.random(Provider.class); + Provider provider = RandomBeans.random(Provider.class); provider.setCurrent(true); providerDao.save(provider); providerDao.updateNotCurrent(provider.getProviderRefId()); - WithdrawalProvider withdrawalProvider = dev.vality.testcontainers.annotations.util.RandomBeans.random(WithdrawalProvider.class); + WithdrawalProvider withdrawalProvider = RandomBeans.random(WithdrawalProvider.class); withdrawalProvider.setCurrent(true); withdrawalProviderDao.save(withdrawalProvider); withdrawalProviderDao.updateNotCurrent(withdrawalProvider.getWithdrawalProviderRefId()); - Proxy proxy = dev.vality.testcontainers.annotations.util.RandomBeans.random(Proxy.class); + Proxy proxy = RandomBeans.random(Proxy.class); proxy.setCurrent(true); proxyDao.save(proxy); proxyDao.updateNotCurrent(proxy.getProxyRefId()); - Terminal terminal = dev.vality.testcontainers.annotations.util.RandomBeans.random(Terminal.class); + Terminal terminal = RandomBeans.random(Terminal.class); terminal.setCurrent(true); terminalDao.save(terminal); terminalDao.updateNotCurrent(terminal.getTerminalRefId()); - TermSetHierarchy termSetHierarchy = dev.vality.testcontainers.annotations.util.RandomBeans.random(TermSetHierarchy.class); + TermSetHierarchy termSetHierarchy = RandomBeans.random(TermSetHierarchy.class); termSetHierarchy.setCurrent(true); termSetHierarchyDao.save(termSetHierarchy); termSetHierarchyDao.updateNotCurrent(termSetHierarchy.getTermSetHierarchyRefId()); @@ -190,184 +205,17 @@ public void dominantDaoTest() { terminal.getVersionId(), termSetHierarchy.getVersionId()).max(); - Assertions.assertEquals(maxVersionId.getAsLong(), lastVersionId.longValue()); - } - - @Test - public void differentCashFlowAggregateFunctionTest() { - jdbcTemplate.execute("truncate table nw.cash_flow cascade"); - List cashFlows = new ArrayList<>(); - - CashFlow cashFlowPaymentAmount = - DaoUtils.createCashFlow(1L, 1000L, "RUB", 1L, CashFlowAccount.provider, "settlement", 2L, - CashFlowAccount.merchant, "settlement", PaymentChangeType.payment); - cashFlows.add(cashFlowPaymentAmount); - CashFlow cashFlowPaymentFee = - DaoUtils.createCashFlow(1L, 10L, "RUB", 2L, CashFlowAccount.merchant, "settlement", 2L, CashFlowAccount.system, - "settlement", PaymentChangeType.payment); - cashFlows.add(cashFlowPaymentFee); - CashFlow cashFlowPaymentExternalIncomeFee = - DaoUtils.createCashFlow(1L, 3L, "RUB", 2L, CashFlowAccount.system, "settlement", 3L, CashFlowAccount.external, - "income", PaymentChangeType.payment); - cashFlows.add(cashFlowPaymentExternalIncomeFee); - CashFlow cashFlowPaymentExternalOutcomeFee = - DaoUtils.createCashFlow(1L, 3L, "RUB", 2L, CashFlowAccount.system, "settlement", 4L, CashFlowAccount.external, - "outcome", PaymentChangeType.payment); - cashFlows.add(cashFlowPaymentExternalOutcomeFee); - CashFlow cashFlowPaymentProviderFee = - DaoUtils.createCashFlow(1L, 3L, "RUB", 2L, CashFlowAccount.system, "settlement", 5L, CashFlowAccount.provider, - "settlement", PaymentChangeType.payment); - cashFlows.add(cashFlowPaymentProviderFee); - CashFlow cashFlowPaymentGuaranteeDeposit = - DaoUtils.createCashFlow(1L, 30L, "RUB", 2L, CashFlowAccount.merchant, "settlement", 5L, CashFlowAccount.merchant, - "guarantee", PaymentChangeType.payment); - cashFlows.add(cashFlowPaymentGuaranteeDeposit); - CashFlow cashFlowRefundAmount = DaoUtils.createCashFlow(1L, 1000L, "RUB", 2L, CashFlowAccount.merchant, "settlement", 5L, - CashFlowAccount.provider, "settlement", PaymentChangeType.refund); - cashFlows.add(cashFlowRefundAmount); - CashFlow cashFlowRefundFee = - DaoUtils.createCashFlow(1L, 10L, "RUB", 2L, CashFlowAccount.merchant, "settlement", 2L, CashFlowAccount.system, - "settlement", PaymentChangeType.refund); - cashFlows.add(cashFlowRefundFee); - CashFlow cashFlowRefundExternalIncomeFee = - DaoUtils.createCashFlow(1L, 3L, "RUB", 2L, CashFlowAccount.system, "settlement", 3L, CashFlowAccount.external, - "income", PaymentChangeType.refund); - cashFlows.add(cashFlowRefundExternalIncomeFee); - CashFlow cashFlowRefundExternalOutcomeFee = - DaoUtils.createCashFlow(1L, 3L, "RUB", 2L, CashFlowAccount.system, "settlement", 4L, CashFlowAccount.external, - "outcome", PaymentChangeType.refund); - cashFlows.add(cashFlowRefundExternalOutcomeFee); - CashFlow cashFlowRefundProviderFee = - DaoUtils.createCashFlow(1L, 3L, "RUB", 2L, CashFlowAccount.system, "settlement", 5L, CashFlowAccount.provider, - "settlement", PaymentChangeType.refund); - cashFlows.add(cashFlowRefundProviderFee); - CashFlow cashFlowPayoutAmount = DaoUtils.createCashFlow(1L, 1000L, "RUB", 2L, CashFlowAccount.merchant, "settlement", 7L, - CashFlowAccount.merchant, "payout", PaymentChangeType.payout); - cashFlows.add(cashFlowPayoutAmount); - CashFlow cashFlowPayoutFee = - DaoUtils.createCashFlow(1L, 10L, "RUB", 1L, CashFlowAccount.merchant, "settlement", 2L, CashFlowAccount.system, - "settlement", PaymentChangeType.payout); - cashFlows.add(cashFlowPayoutFee); - CashFlow cashFlowPayoutFixedFee = - DaoUtils.createCashFlow(1L, 100L, "RUB", 7L, CashFlowAccount.merchant, "payout", 2L, CashFlowAccount.system, - "settlement", PaymentChangeType.payout); - cashFlows.add(cashFlowPayoutFixedFee); - CashFlow cashFlowAdjustmentAmount = - DaoUtils.createCashFlow(1L, 1000L, "RUB", 1L, CashFlowAccount.provider, "settlement", 2L, - CashFlowAccount.merchant, "settlement", PaymentChangeType.adjustment); - cashFlowAdjustmentAmount.setAdjFlowType(AdjustmentCashFlowType.new_cash_flow); - cashFlows.add(cashFlowAdjustmentAmount); - - cashFlowDao.save(cashFlows); - - Assertions.assertEquals(cashFlowPaymentAmount.getAmount(), jdbcTemplate - .queryForObject("SELECT nw.get_payment_amount(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(cashFlowPaymentFee.getAmount(), jdbcTemplate - .queryForObject("SELECT nw.get_payment_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(cashFlowPaymentExternalIncomeFee.getAmount() + cashFlowPaymentExternalOutcomeFee.getAmount(), (long) jdbcTemplate.queryForObject( - "SELECT nw.get_payment_external_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(cashFlowPaymentProviderFee.getAmount(), jdbcTemplate - .queryForObject("SELECT nw.get_payment_provider_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(cashFlowPaymentGuaranteeDeposit.getAmount(), jdbcTemplate.queryForObject( - "SELECT nw.get_payment_guarantee_deposit(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - - Assertions.assertEquals(cashFlowRefundAmount.getAmount(), jdbcTemplate - .queryForObject("SELECT nw.get_refund_amount(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(cashFlowRefundFee.getAmount(), jdbcTemplate - .queryForObject("SELECT nw.get_refund_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(cashFlowRefundExternalIncomeFee.getAmount() + cashFlowRefundExternalOutcomeFee.getAmount(), (long) jdbcTemplate.queryForObject( - "SELECT nw.get_refund_external_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(cashFlowRefundProviderFee.getAmount(), jdbcTemplate - .queryForObject("SELECT nw.get_refund_provider_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - - Assertions.assertEquals(cashFlowPayoutAmount.getAmount(), jdbcTemplate - .queryForObject("SELECT nw.get_payout_amount(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(cashFlowPayoutFixedFee.getAmount(), jdbcTemplate - .queryForObject("SELECT nw.get_payout_fixed_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(cashFlowPayoutFee.getAmount(), jdbcTemplate - .queryForObject("SELECT nw.get_payout_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - } - - @Test - public void whenDataNotFoundCashFlowAggregateFunctionTest() { - jdbcTemplate.execute("truncate table nw.cash_flow cascade"); - CashFlow notCashFlow = dev.vality.testcontainers.annotations.util.RandomBeans.random(CashFlow.class, "objId"); - notCashFlow.setObjId(1L); - cashFlowDao.save(Collections.singletonList(notCashFlow)); - - Assertions.assertEquals(0L, (long) jdbcTemplate - .queryForObject("SELECT nw.get_payment_amount(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(0L, (long) jdbcTemplate - .queryForObject("SELECT nw.get_payment_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(0L, (long) jdbcTemplate - .queryForObject("SELECT nw.get_payment_external_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(0L, (long) jdbcTemplate - .queryForObject("SELECT nw.get_payment_provider_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(0L, (long) jdbcTemplate.queryForObject( - "SELECT nw.get_payment_guarantee_deposit(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - - Assertions.assertEquals(0L, (long) jdbcTemplate - .queryForObject("SELECT nw.get_refund_amount(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(0L, (long) jdbcTemplate - .queryForObject("SELECT nw.get_refund_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(0L, (long) jdbcTemplate - .queryForObject("SELECT nw.get_refund_external_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(0L, (long) jdbcTemplate - .queryForObject("SELECT nw.get_refund_provider_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - - Assertions.assertEquals(0L, (long) jdbcTemplate - .queryForObject("SELECT nw.get_payout_amount(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(0L, (long) jdbcTemplate - .queryForObject("SELECT nw.get_payout_fixed_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(0L, (long) jdbcTemplate - .queryForObject("SELECT nw.get_payout_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - - Assertions.assertEquals(0L, (long) jdbcTemplate - .queryForObject("SELECT nw.get_adjustment_amount(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(0L, (long) jdbcTemplate - .queryForObject("SELECT nw.get_adjustment_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(0L, (long) jdbcTemplate.queryForObject( - "SELECT nw.get_adjustment_external_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); - Assertions.assertEquals(0L, (long) jdbcTemplate.queryForObject( - "SELECT nw.get_adjustment_provider_fee(nw.cash_flow.*) FROM nw.cash_flow WHERE obj_id = 1", - new SingleColumnRowMapper<>(Long.class))); + assertEquals(maxVersionId.getAsLong(), lastVersionId.longValue()); } @Test public void cashFlowDaoTest() { - jdbcTemplate.execute("truncate table nw.payment cascade"); - jdbcTemplate.execute("truncate table nw.cash_flow cascade"); - Long pmntId = 123L; - List cashFlowList = dev.vality.testcontainers.annotations.util.RandomBeans.randomListOf(100, CashFlow.class); + jdbcTemplate.execute("truncate table dw.cash_flow_link cascade"); + jdbcTemplate.execute("truncate table dw.cash_flow cascade"); + Long cashFlowLink = 123L; + List cashFlowList = RandomBeans.randomListOf(100, CashFlow.class); cashFlowList.forEach(cf -> { - cf.setObjId(pmntId); + cf.setObjId(cashFlowLink); cf.setAmount((long) new Random().nextInt(100)); cf.setObjType(PaymentChangeType.payment); cf.setAdjFlowType(null); @@ -379,71 +227,265 @@ public void cashFlowDaoTest() { } }); cashFlowDao.save(cashFlowList); - List byObjId = cashFlowDao.getByObjId(pmntId, PaymentChangeType.payment); - Assertions.assertEquals(new HashSet(byObjId), new HashSet(cashFlowList)); + List byObjId = cashFlowDao.getByObjId(cashFlowLink, PaymentChangeType.payment); + assertEquals(new HashSet(byObjId), new HashSet(cashFlowList)); + } + + @Test + public void cashFlowDaoLinkTest() { + jdbcTemplate.execute("truncate table dw.cash_flow_link cascade"); + List cashFlowLinks = RandomBeans.randomListOf(2, CashFlowLink.class); + for (int i = 0; i < cashFlowLinks.size(); i++) { + cashFlowLinks.get(i).setId(i + 1L); + cashFlowLinks.get(i).setCurrent(true); + } + cashFlowLinkDao.saveBatch(cashFlowLinks); + CashFlowLink first = cashFlowLinks.get(0); + assertEquals(first, cashFlowLinkDao.get(first.getInvoiceId(), first.getPaymentId())); + CashFlowLink second = cashFlowLinks.get(1); + assertEquals(second, cashFlowLinkDao.get(second.getInvoiceId(), second.getPaymentId())); + + CashFlowLink third = RandomBeans.random(CashFlowLink.class, "id", "current", "invoiceId", "paymentId"); + third.setId(3L); + third.setCurrent(false); + third.setInvoiceId(first.getInvoiceId()); + third.setPaymentId(first.getPaymentId()); + cashFlowLinkDao.saveBatch(List.of(third)); + assertEquals(first, cashFlowLinkDao.get(third.getInvoiceId(), third.getPaymentId())); + cashFlowLinkDao.switchCurrent(Set.of(InvoicingKey.buildKey(third.getInvoiceId(), third.getPaymentId()))); + third.setCurrent(true); + assertEquals(third, cashFlowLinkDao.get(third.getInvoiceId(), third.getPaymentId())); } @Test public void adjustmentDaoTest() { - jdbcTemplate.execute("truncate table nw.adjustment cascade"); - Adjustment adjustment = dev.vality.testcontainers.annotations.util.RandomBeans.random(Adjustment.class); + jdbcTemplate.execute("truncate table dw.adjustment cascade"); + Adjustment adjustment = RandomBeans.random(Adjustment.class); adjustment.setCurrent(true); adjustmentDao.save(adjustment); - Assertions.assertEquals(adjustment.getPartyId(), adjustmentDao.get(adjustment.getInvoiceId(), adjustment.getPaymentId(), adjustment.getAdjustmentId()) + assertEquals(adjustment.getPartyId(), adjustmentDao.get(adjustment.getInvoiceId(), adjustment.getPaymentId(), adjustment.getAdjustmentId()) .getPartyId()); adjustmentDao.updateNotCurrent(adjustment.getId()); assertThrows(NotFoundException.class, () -> adjustmentDao.get(adjustment.getInvoiceId(), adjustment.getPaymentId(), adjustment.getAdjustmentId())); } + @Test + void invoiceDaoTest() { + jdbcTemplate.execute("truncate table dw.invoice cascade"); + List invoices = RandomBeans.randomListOf(3, Invoice.class); + invoiceDao.saveBatch(invoices); + assertEquals(invoices.get(0), invoiceDao.get(invoices.get(0).getInvoiceId())); + assertEquals(invoices.get(1), invoiceDao.get(invoices.get(1).getInvoiceId())); + assertEquals(invoices.get(2), invoiceDao.get(invoices.get(2).getInvoiceId())); + } + + + @Test + void invoiceStatusInfoDaoTest() { + jdbcTemplate.execute("truncate table dw.invoice_status_info cascade"); + List statusInfos = + RandomBeans.randomListOf(3, InvoiceStatusInfo.class); + statusInfos.forEach(status -> status.setCurrent(true)); + invoiceStatusInfoDao.saveBatch(statusInfos); + assertEquals(statusInfos.get(0), invoiceStatusInfoDao.get(statusInfos.get(0).getInvoiceId())); + assertEquals(statusInfos.get(1), invoiceStatusInfoDao.get(statusInfos.get(1).getInvoiceId())); + assertEquals(statusInfos.get(2), invoiceStatusInfoDao.get(statusInfos.get(2).getInvoiceId())); + + InvoiceStatusInfo statusInfo = RandomBeans.random(InvoiceStatusInfo.class); + InvoiceStatusInfo initialStatusInfo = statusInfos.get(0); + statusInfo.setInvoiceId(initialStatusInfo.getInvoiceId()); + statusInfo.setCurrent(false); + statusInfo.setId(initialStatusInfo.getId() + 1); + invoiceStatusInfoDao.saveBatch(List.of(statusInfo)); + assertNotEquals(statusInfo, initialStatusInfo); + assertEquals(initialStatusInfo, invoiceStatusInfoDao.get(initialStatusInfo.getInvoiceId())); + invoiceStatusInfoDao.switchCurrent(Set.of(statusInfo.getInvoiceId())); + statusInfo.setCurrent(true); + assertEquals(statusInfo, invoiceStatusInfoDao.get(initialStatusInfo.getInvoiceId())); + } + @Test public void invoiceCartDaoTest() { - jdbcTemplate.execute("truncate table nw.invoice cascade"); - jdbcTemplate.execute("truncate table nw.invoice_cart cascade"); - Invoice invoice = dev.vality.testcontainers.annotations.util.RandomBeans.random(Invoice.class); - invoice.setCurrent(true); - Long invId = invoice.getId(); - invoiceDao.saveBatch(Collections.singletonList(invoice)); - List invoiceCarts = dev.vality.testcontainers.annotations.util.RandomBeans.randomListOf(10, InvoiceCart.class); - invoiceCarts.forEach(ic -> ic.setInvId(invId)); + jdbcTemplate.execute("truncate table dw.invoice_cart cascade"); + String invoiceId = UUID.randomUUID().toString(); + List invoiceCarts = RandomBeans.randomListOf(10, InvoiceCart.class); + invoiceCarts.forEach(ic -> ic.setInvoiceId(invoiceId)); invoiceCartDao.save(invoiceCarts); - List byInvId = invoiceCartDao.getByInvId(invId); - Assertions.assertEquals(new HashSet(invoiceCarts), new HashSet(byInvId)); - + assertEquals(invoiceCarts, invoiceCartDao.getByInvoiceId(invoiceId)); } @Test public void paymentDaoTest() { - jdbcTemplate.execute("truncate table nw.payment cascade"); - Payment payment = dev.vality.testcontainers.annotations.util.RandomBeans.random(Payment.class); - payment.setId(1L); - payment.setCurrent(false); - Payment paymentTwo = dev.vality.testcontainers.annotations.util.RandomBeans.random(Payment.class); - paymentTwo.setId(2L); - paymentTwo.setCurrent(false); - paymentTwo.setInvoiceId(payment.getInvoiceId()); - paymentTwo.setPaymentId(payment.getPaymentId()); - paymentDao.saveBatch(Arrays.asList(payment, paymentTwo)); - paymentDao.switchCurrent(Collections.singletonList( - new InvoicingKey(payment.getInvoiceId(), payment.getPaymentId(), InvoicingType.PAYMENT))); - Payment paymentGet = paymentDao.get(payment.getInvoiceId(), payment.getPaymentId()); - paymentTwo.setCurrent(true); - Assertions.assertEquals(paymentTwo, paymentGet); - paymentTwo.setPartyRevision(1111L); - paymentDao.updateBatch(Collections.singletonList(paymentTwo)); - Payment paymentGet2 = paymentDao.get(payment.getInvoiceId(), payment.getPaymentId()); - Assertions.assertEquals(paymentTwo, paymentGet2); + jdbcTemplate.execute("truncate table dw.payment cascade"); + Payment first = RandomBeans.random(Payment.class); + first.setId(1L); + Payment second = RandomBeans.random(Payment.class); + second.setId(2L); + paymentDao.saveBatch(Arrays.asList(first, second)); + assertEquals(first, paymentDao.get(first.getInvoiceId(), first.getPaymentId())); + assertEquals(second, paymentDao.get(second.getInvoiceId(), second.getPaymentId())); + } + + @Test + public void paymentStatusInfoDaoTest() { + jdbcTemplate.execute("truncate table dw.payment_status_info cascade"); + List statusInfos = RandomBeans.randomListOf(2, PaymentStatusInfo.class); + statusInfos.forEach(statusInfo -> statusInfo.setCurrent(true)); + paymentStatusInfoDao.saveBatch(statusInfos); + PaymentStatusInfo first = statusInfos.get(0); + assertEquals(first, paymentStatusInfoDao.get(first.getInvoiceId(), first.getPaymentId())); + PaymentStatusInfo second = statusInfos.get(1); + assertEquals(second, paymentStatusInfoDao.get(second.getInvoiceId(), second.getPaymentId())); + + PaymentStatusInfo third = RandomBeans.random(PaymentStatusInfo.class); + third.setId(first.getId() + 1); + third.setCurrent(false); + third.setInvoiceId(first.getInvoiceId()); + third.setPaymentId(first.getPaymentId()); + paymentStatusInfoDao.saveBatch(List.of(third)); + assertEquals(first, paymentStatusInfoDao.get(third.getInvoiceId(), third.getPaymentId())); + paymentStatusInfoDao.switchCurrent(Set.of(InvoicingKey.buildKey(third.getInvoiceId(), third.getPaymentId()))); + third.setCurrent(true); + assertEquals(third, paymentStatusInfoDao.get(third.getInvoiceId(), third.getPaymentId())); + } + + @Test + public void paymentPayerInfoDaoTest() { + jdbcTemplate.execute("truncate table dw.payment_payer_info cascade"); + PaymentPayerInfo first = RandomBeans.random(PaymentPayerInfo.class); + first.setId(1L); + PaymentPayerInfo second = RandomBeans.random(PaymentPayerInfo.class); + second.setId(2L); + paymentPayerInfoDao.saveBatch(Arrays.asList(first, second)); + assertEquals(first, paymentPayerInfoDao.get(first.getInvoiceId(), first.getPaymentId())); + assertEquals(second, paymentPayerInfoDao.get(second.getInvoiceId(), second.getPaymentId())); + } + + @Test + public void paymentAdditionalInfoDaoTest() { + jdbcTemplate.execute("truncate table dw.payment_additional_info cascade"); + List list = RandomBeans.randomListOf(2, PaymentAdditionalInfo.class); + list.forEach(statusInfo -> statusInfo.setCurrent(true)); + paymentAdditionalInfoDao.saveBatch(list); + PaymentAdditionalInfo first = list.get(0); + assertEquals(first, paymentAdditionalInfoDao.get(first.getInvoiceId(), first.getPaymentId())); + PaymentAdditionalInfo second = list.get(1); + assertEquals(second, paymentAdditionalInfoDao.get(second.getInvoiceId(), second.getPaymentId())); + + PaymentAdditionalInfo third = RandomBeans.random(PaymentAdditionalInfo.class); + third.setId(first.getId() + 1); + third.setCurrent(false); + third.setInvoiceId(first.getInvoiceId()); + third.setPaymentId(first.getPaymentId()); + paymentAdditionalInfoDao.saveBatch(List.of(third)); + assertEquals(first, paymentAdditionalInfoDao.get(third.getInvoiceId(), third.getPaymentId())); + paymentAdditionalInfoDao.switchCurrent(Set.of(InvoicingKey.buildKey(third.getInvoiceId(), third.getPaymentId()))); + third.setCurrent(true); + assertEquals(third, paymentAdditionalInfoDao.get(third.getInvoiceId(), third.getPaymentId())); + } + + @Test + public void paymentRecurrentInfoDaoTest() { + jdbcTemplate.execute("truncate table dw.payment_recurrent_info cascade"); + List list = RandomBeans.randomListOf(2, PaymentRecurrentInfo.class); + list.forEach(statusInfo -> statusInfo.setCurrent(true)); + paymentRecurrentInfoDao.saveBatch(list); + PaymentRecurrentInfo first = list.get(0); + assertEquals(first, paymentRecurrentInfoDao.get(first.getInvoiceId(), first.getPaymentId())); + PaymentRecurrentInfo second = list.get(1); + assertEquals(second, paymentRecurrentInfoDao.get(second.getInvoiceId(), second.getPaymentId())); + + PaymentRecurrentInfo third = RandomBeans.random(PaymentRecurrentInfo.class); + third.setId(first.getId() + 1); + third.setCurrent(false); + third.setInvoiceId(first.getInvoiceId()); + third.setPaymentId(first.getPaymentId()); + paymentRecurrentInfoDao.saveBatch(List.of(third)); + assertEquals(first, paymentRecurrentInfoDao.get(third.getInvoiceId(), third.getPaymentId())); + paymentRecurrentInfoDao.switchCurrent(Set.of(InvoicingKey.buildKey(third.getInvoiceId(), third.getPaymentId()))); + third.setCurrent(true); + assertEquals(third, paymentRecurrentInfoDao.get(third.getInvoiceId(), third.getPaymentId())); + } + + @Test + public void paymentRiskDataDaoTest() { + jdbcTemplate.execute("truncate table dw.payment_risk_data cascade"); + List list = RandomBeans.randomListOf(2, PaymentRiskData.class); + list.forEach(statusInfo -> statusInfo.setCurrent(true)); + paymentRiskDataDao.saveBatch(list); + PaymentRiskData first = list.get(0); + assertEquals(first, paymentRiskDataDao.get(first.getInvoiceId(), first.getPaymentId())); + PaymentRiskData second = list.get(1); + assertEquals(second, paymentRiskDataDao.get(second.getInvoiceId(), second.getPaymentId())); + + PaymentRiskData third = RandomBeans.random(PaymentRiskData.class); + third.setId(first.getId() + 1); + third.setCurrent(false); + third.setInvoiceId(first.getInvoiceId()); + third.setPaymentId(first.getPaymentId()); + paymentRiskDataDao.saveBatch(List.of(third)); + assertEquals(first, paymentRiskDataDao.get(third.getInvoiceId(), third.getPaymentId())); + paymentRiskDataDao.switchCurrent(Set.of(InvoicingKey.buildKey(third.getInvoiceId(), third.getPaymentId()))); + third.setCurrent(true); + assertEquals(third, paymentRiskDataDao.get(third.getInvoiceId(), third.getPaymentId())); + } + + @Test + public void paymentFeeDaoTest() { + jdbcTemplate.execute("truncate table dw.payment_fee cascade"); + List list = RandomBeans.randomListOf(2, PaymentFee.class); + list.forEach(statusInfo -> statusInfo.setCurrent(true)); + paymentFeeDao.saveBatch(list); + PaymentFee first = list.get(0); + assertEquals(first, paymentFeeDao.get(first.getInvoiceId(), first.getPaymentId())); + PaymentFee second = list.get(1); + assertEquals(second, paymentFeeDao.get(second.getInvoiceId(), second.getPaymentId())); + + PaymentFee third = RandomBeans.random(PaymentFee.class); + third.setId(first.getId() + 1); + third.setCurrent(false); + third.setInvoiceId(first.getInvoiceId()); + third.setPaymentId(first.getPaymentId()); + paymentFeeDao.saveBatch(List.of(third)); + assertEquals(first, paymentFeeDao.get(third.getInvoiceId(), third.getPaymentId())); + paymentFeeDao.switchCurrent(Set.of(InvoicingKey.buildKey(third.getInvoiceId(), third.getPaymentId()))); + third.setCurrent(true); + assertEquals(third, paymentFeeDao.get(third.getInvoiceId(), third.getPaymentId())); + } + + @Test + public void paymentRouteDaoTest() { + jdbcTemplate.execute("truncate table dw.payment_route cascade"); + List list = RandomBeans.randomListOf(2, PaymentRoute.class); + list.forEach(statusInfo -> statusInfo.setCurrent(true)); + paymentRouteDao.saveBatch(list); + PaymentRoute first = list.get(0); + assertEquals(first, paymentRouteDao.get(first.getInvoiceId(), first.getPaymentId())); + PaymentRoute second = list.get(1); + assertEquals(second, paymentRouteDao.get(second.getInvoiceId(), second.getPaymentId())); + + PaymentRoute third = RandomBeans.random(PaymentRoute.class); + third.setId(first.getId() + 1); + third.setCurrent(false); + third.setInvoiceId(first.getInvoiceId()); + third.setPaymentId(first.getPaymentId()); + paymentRouteDao.saveBatch(List.of(third)); + assertEquals(first, paymentRouteDao.get(third.getInvoiceId(), third.getPaymentId())); + paymentRouteDao.switchCurrent(Set.of(InvoicingKey.buildKey(third.getInvoiceId(), third.getPaymentId()))); + third.setCurrent(true); + assertEquals(third, paymentRouteDao.get(third.getInvoiceId(), third.getPaymentId())); } @Test public void refundDaoTest() { - jdbcTemplate.execute("truncate table nw.refund cascade"); - Refund refund = dev.vality.testcontainers.annotations.util.RandomBeans.random(Refund.class); + jdbcTemplate.execute("truncate table dw.refund cascade"); + Refund refund = RandomBeans.random(Refund.class); refund.setCurrent(true); refundDao.save(refund); Refund refundGet = refundDao.get(refund.getInvoiceId(), refund.getPaymentId(), refund.getRefundId()); - Assertions.assertEquals(refund, refundGet); + assertEquals(refund, refundGet); refundDao.updateNotCurrent(refund.getId()); assertThrows(NotFoundException.class, () -> refundDao.get(refund.getInvoiceId(), refund.getPaymentId(), refund.getRefundId())); @@ -451,36 +493,36 @@ public void refundDaoTest() { @Test public void contractAdjustmentDaoTest() { - jdbcTemplate.execute("truncate table nw.contract_adjustment cascade"); - jdbcTemplate.execute("truncate table nw.contract cascade"); - Contract contract = dev.vality.testcontainers.annotations.util.RandomBeans.random(Contract.class); + jdbcTemplate.execute("truncate table dw.contract_adjustment cascade"); + jdbcTemplate.execute("truncate table dw.contract cascade"); + Contract contract = RandomBeans.random(Contract.class); contract.setCurrent(true); Long cntrctId = contractDao.save(contract).get(); - List contractAdjustments = dev.vality.testcontainers.annotations.util.RandomBeans.randomListOf(10, ContractAdjustment.class); + List contractAdjustments = RandomBeans.randomListOf(10, ContractAdjustment.class); contractAdjustments.forEach(ca -> ca.setCntrctId(cntrctId)); contractAdjustmentDao.save(contractAdjustments); List byCntrctId = contractAdjustmentDao.getByCntrctId(cntrctId); - Assertions.assertEquals(new HashSet(contractAdjustments), new HashSet(byCntrctId)); + assertEquals(new HashSet(contractAdjustments), new HashSet(byCntrctId)); } @Test public void contractDaoTest() { - jdbcTemplate.execute("truncate table nw.contract cascade"); - Contract contract = dev.vality.testcontainers.annotations.util.RandomBeans.random(Contract.class); + jdbcTemplate.execute("truncate table dw.contract cascade"); + Contract contract = RandomBeans.random(Contract.class); contract.setCurrent(true); contractDao.save(contract); Contract contractGet = contractDao.get(contract.getPartyId(), contract.getContractId()); - Assertions.assertEquals(contract, contractGet); + assertEquals(contract, contractGet); } @Test public void contractorDaoTest() { - jdbcTemplate.execute("truncate table nw.contractor cascade"); - Contractor contractor = dev.vality.testcontainers.annotations.util.RandomBeans.random(Contractor.class); + jdbcTemplate.execute("truncate table dw.contractor cascade"); + Contractor contractor = RandomBeans.random(Contractor.class); contractor.setCurrent(true); contractorDao.save(contractor); Contractor contractorGet = contractorDao.get(contractor.getPartyId(), contractor.getContractorId()); - Assertions.assertEquals(contractor, contractorGet); + assertEquals(contractor, contractorGet); Integer changeId = contractor.getChangeId() + 1; contractor.setChangeId(changeId); Long oldId = contractor.getId(); @@ -491,12 +533,12 @@ public void contractorDaoTest() { @Test public void partyDaoTest() { - jdbcTemplate.execute("truncate table nw.party cascade"); - Party party = dev.vality.testcontainers.annotations.util.RandomBeans.random(Party.class); + jdbcTemplate.execute("truncate table dw.party cascade"); + Party party = RandomBeans.random(Party.class); party.setCurrent(true); partyDao.save(party); Party partyGet = partyDao.get(party.getPartyId()); - Assertions.assertEquals(party, partyGet); + assertEquals(party, partyGet); Long oldId = party.getId(); Integer changeId = party.getChangeId() + 1; @@ -506,31 +548,31 @@ public void partyDaoTest() { partyDao.updateNotCurrent(oldId); partyGet = partyDao.get(party.getPartyId()); - Assertions.assertEquals(changeId, partyGet.getChangeId()); + assertEquals(changeId, partyGet.getChangeId()); } @Test public void payoutToolDaoTest() { - jdbcTemplate.execute("truncate table nw.contract cascade"); - jdbcTemplate.execute("truncate table nw.payout_tool cascade"); - Contract contract = dev.vality.testcontainers.annotations.util.RandomBeans.random(Contract.class); + jdbcTemplate.execute("truncate table dw.contract cascade"); + jdbcTemplate.execute("truncate table dw.payout_tool cascade"); + Contract contract = RandomBeans.random(Contract.class); contract.setCurrent(true); Long cntrctId = contractDao.save(contract).get(); - List payoutTools = dev.vality.testcontainers.annotations.util.RandomBeans.randomListOf(10, PayoutTool.class); + List payoutTools = RandomBeans.randomListOf(10, PayoutTool.class); payoutTools.forEach(pt -> pt.setCntrctId(cntrctId)); payoutToolDao.save(payoutTools); List byCntrctId = payoutToolDao.getByCntrctId(cntrctId); - Assertions.assertEquals(new HashSet(payoutTools), new HashSet(byCntrctId)); + assertEquals(new HashSet(payoutTools), new HashSet(byCntrctId)); } @Test public void shopDaoTest() { - jdbcTemplate.execute("truncate table nw.shop cascade"); - Shop shop = dev.vality.testcontainers.annotations.util.RandomBeans.random(Shop.class); + jdbcTemplate.execute("truncate table dw.shop cascade"); + Shop shop = RandomBeans.random(Shop.class); shop.setCurrent(true); shopDao.save(shop); Shop shopGet = shopDao.get(shop.getPartyId(), shop.getShopId()); - Assertions.assertEquals(shop, shopGet); + assertEquals(shop, shopGet); Integer changeId = shop.getChangeId() + 1; shop.setChangeId(changeId); @@ -542,14 +584,14 @@ public void shopDaoTest() { @Test public void rateDaoTest() { - jdbcTemplate.execute("truncate table nw.rate cascade"); - Rate rate = dev.vality.testcontainers.annotations.util.RandomBeans.random(Rate.class); + jdbcTemplate.execute("truncate table dw.rate cascade"); + Rate rate = RandomBeans.random(Rate.class); rate.setCurrent(true); Long id = rateDao.save(rate); rate.setId(id); - Assertions.assertEquals(rate, jdbcTemplate.queryForObject( - "SELECT * FROM nw.rate WHERE id = ? ", + assertEquals(rate, jdbcTemplate.queryForObject( + "SELECT * FROM dw.rate WHERE id = ? ", new Object[]{id}, new BeanPropertyRowMapper(Rate.class) )); @@ -557,12 +599,12 @@ public void rateDaoTest() { List ids = rateDao.getIds(rate.getSourceId()); Assertions.assertNotNull(ids); Assertions.assertFalse(ids.isEmpty()); - Assertions.assertEquals(1, ids.size()); - Assertions.assertEquals(id, ids.get(0)); + assertEquals(1, ids.size()); + assertEquals(id, ids.get(0)); rateDao.updateNotCurrent(Collections.singletonList(id)); assertThrows(EmptyResultDataAccessException.class, () -> jdbcTemplate.queryForObject( - "SELECT * FROM nw.rate AS rate WHERE rate.id = ? AND rate.current", + "SELECT * FROM dw.rate AS rate WHERE rate.id = ? AND rate.current", new Object[]{id}, new BeanPropertyRowMapper(Rate.class) )); @@ -573,13 +615,13 @@ public void getIntHashTest() { Integer javaHash = HashUtil.getIntHash("kek"); Integer postgresHash = jdbcTemplate.queryForObject("select ('x0'||substr(md5('kek'), 1, 7))::bit(32)::int", Integer.class); - Assertions.assertEquals(javaHash, postgresHash); + assertEquals(javaHash, postgresHash); } @Test public void constraintTests() { - jdbcTemplate.execute("truncate table nw.adjustment cascade"); - Adjustment adjustment = dev.vality.testcontainers.annotations.util.RandomBeans.random(Adjustment.class); + jdbcTemplate.execute("truncate table dw.adjustment cascade"); + Adjustment adjustment = RandomBeans.random(Adjustment.class); adjustment.setChangeId(1); adjustment.setSequenceId(1L); adjustment.setInvoiceId("1"); @@ -587,33 +629,33 @@ public void constraintTests() { adjustment.setCurrent(true); adjustmentDao.save(adjustment); - Assertions.assertEquals("1", adjustmentDao.get(adjustment.getInvoiceId(), adjustment.getPaymentId(), adjustment.getAdjustmentId()) + assertEquals("1", adjustmentDao.get(adjustment.getInvoiceId(), adjustment.getPaymentId(), adjustment.getAdjustmentId()) .getPartyId()); adjustment.setPartyId("2"); adjustmentDao.save(adjustment); - Assertions.assertEquals("1", adjustmentDao.get(adjustment.getInvoiceId(), adjustment.getPaymentId(), adjustment.getAdjustmentId()) + assertEquals("1", adjustmentDao.get(adjustment.getInvoiceId(), adjustment.getPaymentId(), adjustment.getAdjustmentId()) .getPartyId()); } @Test public void idsGeneratorTest() { List list = idsGeneratorDao.get(100); - Assertions.assertEquals(100, list.size()); - Assertions.assertEquals(99, list.get(99) - list.get(0)); + assertEquals(100, list.size()); + assertEquals(99, list.get(99) - list.get(0)); } @Test public void recurrentPaymentToolDaoTest() { - jdbcTemplate.execute("truncate table nw.recurrent_payment_tool cascade"); - RecurrentPaymentTool recurrentPaymentTool = dev.vality.testcontainers.annotations.util.RandomBeans.random(RecurrentPaymentTool.class); + jdbcTemplate.execute("truncate table dw.recurrent_payment_tool cascade"); + RecurrentPaymentTool recurrentPaymentTool = RandomBeans.random(RecurrentPaymentTool.class); recurrentPaymentTool.setCurrent(true); Optional id = recurrentPaymentToolDao.save(recurrentPaymentTool); Assertions.assertTrue(id.isPresent()); recurrentPaymentTool.setId(id.get()); - Assertions.assertEquals(recurrentPaymentTool, recurrentPaymentToolDao.get(recurrentPaymentTool.getRecurrentPaymentToolId())); + assertEquals(recurrentPaymentTool, recurrentPaymentToolDao.get(recurrentPaymentTool.getRecurrentPaymentToolId())); recurrentPaymentToolDao.updateNotCurrent(recurrentPaymentTool.getId()); assertThrows(NotFoundException.class, () -> recurrentPaymentToolDao.get(recurrentPaymentTool.getRecurrentPaymentToolId())); diff --git a/src/test/java/dev/vality/newway/dao/DepositDaoTest.java b/src/test/java/dev/vality/newway/dao/DepositDaoTest.java index 8d817e32..fe6b728f 100644 --- a/src/test/java/dev/vality/newway/dao/DepositDaoTest.java +++ b/src/test/java/dev/vality/newway/dao/DepositDaoTest.java @@ -22,7 +22,7 @@ public class DepositDaoTest { @Test public void depositDaoTest() { - jdbcTemplate.execute("truncate table nw.deposit cascade"); + jdbcTemplate.execute("truncate table dw.deposit cascade"); Deposit deposit = dev.vality.testcontainers.annotations.util.RandomBeans.random(Deposit.class); deposit.setCurrent(true); Long id = depositDao.save(deposit).get(); diff --git a/src/test/java/dev/vality/newway/dao/DestinationDaoTest.java b/src/test/java/dev/vality/newway/dao/DestinationDaoTest.java index 87bf4cd7..3dda8dab 100644 --- a/src/test/java/dev/vality/newway/dao/DestinationDaoTest.java +++ b/src/test/java/dev/vality/newway/dao/DestinationDaoTest.java @@ -22,7 +22,7 @@ public class DestinationDaoTest { @Test public void destinationDaoTest() { - jdbcTemplate.execute("truncate table nw.destination cascade"); + jdbcTemplate.execute("truncate table dw.destination cascade"); Destination destination = dev.vality.testcontainers.annotations.util.RandomBeans.random(Destination.class); destination.setCurrent(true); Long id = destinationDao.save(destination).get(); diff --git a/src/test/java/dev/vality/newway/dao/IdentityDaoTest.java b/src/test/java/dev/vality/newway/dao/IdentityDaoTest.java index cbfe3379..0a186d7e 100644 --- a/src/test/java/dev/vality/newway/dao/IdentityDaoTest.java +++ b/src/test/java/dev/vality/newway/dao/IdentityDaoTest.java @@ -22,7 +22,7 @@ public class IdentityDaoTest { @Test public void identityDaoTest() { - jdbcTemplate.execute("truncate table nw.identity cascade"); + jdbcTemplate.execute("truncate table dw.identity cascade"); Identity identity = dev.vality.testcontainers.annotations.util.RandomBeans.random(Identity.class); identity.setCurrent(true); Long id = identityDao.save(identity).get(); diff --git a/src/test/java/dev/vality/newway/dao/PayoutDaoTest.java b/src/test/java/dev/vality/newway/dao/PayoutDaoTest.java index 7b5ab9b1..e15e5b09 100644 --- a/src/test/java/dev/vality/newway/dao/PayoutDaoTest.java +++ b/src/test/java/dev/vality/newway/dao/PayoutDaoTest.java @@ -24,7 +24,7 @@ public class PayoutDaoTest { @Test public void payoutDaoTest() { - jdbcTemplate.execute("truncate table nw.payout cascade"); + jdbcTemplate.execute("truncate table dw.payout cascade"); Payout payout = dev.vality.testcontainers.annotations.util.RandomBeans.random(Payout.class); payout.setCurrent(true); Optional save = payoutDao.save(payout); diff --git a/src/test/java/dev/vality/newway/dao/SourceDaoTest.java b/src/test/java/dev/vality/newway/dao/SourceDaoTest.java index a5252103..02a228d0 100644 --- a/src/test/java/dev/vality/newway/dao/SourceDaoTest.java +++ b/src/test/java/dev/vality/newway/dao/SourceDaoTest.java @@ -22,7 +22,7 @@ public class SourceDaoTest { @Test public void sourceDaoTest() { - jdbcTemplate.execute("truncate table nw.source cascade"); + jdbcTemplate.execute("truncate table dw.source cascade"); Source source = dev.vality.testcontainers.annotations.util.RandomBeans.random(Source.class); source.setCurrent(true); Long id = sourceDao.save(source).get(); diff --git a/src/test/java/dev/vality/newway/dao/WalletDaoTest.java b/src/test/java/dev/vality/newway/dao/WalletDaoTest.java index 007f9df6..109c6294 100644 --- a/src/test/java/dev/vality/newway/dao/WalletDaoTest.java +++ b/src/test/java/dev/vality/newway/dao/WalletDaoTest.java @@ -22,7 +22,7 @@ public class WalletDaoTest { @Test public void walletDaoTest() { - jdbcTemplate.execute("truncate table nw.wallet cascade"); + jdbcTemplate.execute("truncate table dw.wallet cascade"); Wallet wallet = dev.vality.testcontainers.annotations.util.RandomBeans.random(Wallet.class); wallet.setCurrent(true); Long id = walletDao.save(wallet).get(); diff --git a/src/test/java/dev/vality/newway/dao/WithdrawalDaoTest.java b/src/test/java/dev/vality/newway/dao/WithdrawalDaoTest.java index 40b68fd4..0a50f26e 100644 --- a/src/test/java/dev/vality/newway/dao/WithdrawalDaoTest.java +++ b/src/test/java/dev/vality/newway/dao/WithdrawalDaoTest.java @@ -22,7 +22,7 @@ public class WithdrawalDaoTest { @Test public void withdrawalDaoTest() { - jdbcTemplate.execute("truncate table nw.withdrawal cascade"); + jdbcTemplate.execute("truncate table dw.withdrawal cascade"); Withdrawal withdrawal = dev.vality.testcontainers.annotations.util.RandomBeans.random(Withdrawal.class); withdrawal.setCurrent(true); Long id = withdrawalDao.save(withdrawal).get(); diff --git a/src/test/java/dev/vality/newway/dao/WithdrawalSessionDaoTest.java b/src/test/java/dev/vality/newway/dao/WithdrawalSessionDaoTest.java index e3aebe56..4283305f 100644 --- a/src/test/java/dev/vality/newway/dao/WithdrawalSessionDaoTest.java +++ b/src/test/java/dev/vality/newway/dao/WithdrawalSessionDaoTest.java @@ -22,7 +22,7 @@ public class WithdrawalSessionDaoTest { @Test public void withdrawalSessionDao() { - jdbcTemplate.execute("truncate table nw.withdrawal_session cascade"); + jdbcTemplate.execute("truncate table dw.withdrawal_session cascade"); WithdrawalSession withdrawalSession = dev.vality.testcontainers.annotations.util.RandomBeans.random(WithdrawalSession.class); withdrawalSession.setCurrent(true); Long id = withdrawalSessionDao.save(withdrawalSession).get(); diff --git a/src/test/java/dev/vality/newway/handler/event/impl/partymngmnt/party/PartyRevisionChangedHandlerTest.java b/src/test/java/dev/vality/newway/handler/event/impl/partymngmnt/party/PartyRevisionChangedHandlerTest.java index f51a0f22..7a2cef8c 100644 --- a/src/test/java/dev/vality/newway/handler/event/impl/partymngmnt/party/PartyRevisionChangedHandlerTest.java +++ b/src/test/java/dev/vality/newway/handler/event/impl/partymngmnt/party/PartyRevisionChangedHandlerTest.java @@ -118,12 +118,12 @@ public void testPerfomanceHandle() { partyRevisionChangedHandler.handle(change, message, 1); Assertions.assertEquals(Integer.valueOf(CNT), jdbcTemplate - .queryForObject("select count(1) from nw.shop_revision", new MapSqlParameterSource(), Integer.class)); + .queryForObject("select count(1) from dw.shop_revision", new MapSqlParameterSource(), Integer.class)); Assertions.assertEquals(Integer.valueOf(CNT), jdbcTemplate - .queryForObject("select count(1) from nw.contract_revision", new MapSqlParameterSource(), + .queryForObject("select count(1) from dw.contract_revision", new MapSqlParameterSource(), Integer.class)); Assertions.assertEquals(Integer.valueOf(CNT), jdbcTemplate - .queryForObject("select count(1) from nw.contractor_revision", new MapSqlParameterSource(), + .queryForObject("select count(1) from dw.contractor_revision", new MapSqlParameterSource(), Integer.class)); } diff --git a/src/test/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedBankCardHandlerTest.java b/src/test/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedBankCardHandlerTest.java index 88e5bd7d..3e5690ca 100644 --- a/src/test/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedBankCardHandlerTest.java +++ b/src/test/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedBankCardHandlerTest.java @@ -24,7 +24,7 @@ public class DestinationCreatedBankCardHandlerTest { private JdbcTemplate jdbcTemplate; Destination destination = dev.vality.testcontainers.annotations.util.RandomBeans.random(Destination.class); - String sqlStatement = "select * from nw.destination LIMIT 1;"; + String sqlStatement = "select * from dw.destination LIMIT 1;"; @BeforeEach public void setUp() { diff --git a/src/test/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedCryptoWalletHandlerTest.java b/src/test/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedCryptoWalletHandlerTest.java index 8d6ce0bb..b96009a5 100644 --- a/src/test/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedCryptoWalletHandlerTest.java +++ b/src/test/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedCryptoWalletHandlerTest.java @@ -24,7 +24,7 @@ public class DestinationCreatedCryptoWalletHandlerTest { private JdbcTemplate jdbcTemplate; Destination destination = dev.vality.testcontainers.annotations.util.RandomBeans.random(Destination.class); - String sqlStatement = "select * from nw.destination LIMIT 1;"; + String sqlStatement = "select * from dw.destination LIMIT 1;"; @BeforeEach public void setUp() { diff --git a/src/test/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedDigitalWalletHandlerTest.java b/src/test/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedDigitalWalletHandlerTest.java index 808be533..882c501b 100644 --- a/src/test/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedDigitalWalletHandlerTest.java +++ b/src/test/java/dev/vality/newway/handler/event/stock/impl/destination/DestinationCreatedDigitalWalletHandlerTest.java @@ -24,7 +24,7 @@ public class DestinationCreatedDigitalWalletHandlerTest { private JdbcTemplate jdbcTemplate; Destination destination = dev.vality.testcontainers.annotations.util.RandomBeans.random(Destination.class); - String sqlStatement = "select * from nw.destination LIMIT 1;"; + String sqlStatement = "select * from dw.destination LIMIT 1;"; @BeforeEach public void setUp() { diff --git a/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedBankCardHandlerTest.java b/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedBankCardHandlerTest.java index dec08f54..96f9d991 100644 --- a/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedBankCardHandlerTest.java +++ b/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedBankCardHandlerTest.java @@ -26,7 +26,7 @@ public class WithdrawalSessionCreatedBankCardHandlerTest { private JdbcTemplate jdbcTemplate; Destination destination = dev.vality.testcontainers.annotations.util.RandomBeans.random(Destination.class); - String sqlStatement = "select * from nw.withdrawal_session LIMIT 1;"; + String sqlStatement = "select * from dw.withdrawal_session LIMIT 1;"; @BeforeEach public void setUp() { diff --git a/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedCryptoWalletHandlerTest.java b/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedCryptoWalletHandlerTest.java index 952207c3..2876ad8e 100644 --- a/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedCryptoWalletHandlerTest.java +++ b/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedCryptoWalletHandlerTest.java @@ -26,7 +26,7 @@ public class WithdrawalSessionCreatedCryptoWalletHandlerTest { private JdbcTemplate jdbcTemplate; Destination destination = dev.vality.testcontainers.annotations.util.RandomBeans.random(Destination.class); - String sqlStatement = "select * from nw.withdrawal_session LIMIT 1;"; + String sqlStatement = "select * from dw.withdrawal_session LIMIT 1;"; @BeforeEach public void setUp() { diff --git a/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedDigitalWalletHandlerTest.java b/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedDigitalWalletHandlerTest.java index 5e839419..df0d7dae 100644 --- a/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedDigitalWalletHandlerTest.java +++ b/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedDigitalWalletHandlerTest.java @@ -26,7 +26,7 @@ public class WithdrawalSessionCreatedDigitalWalletHandlerTest { private JdbcTemplate jdbcTemplate; Destination destination = dev.vality.testcontainers.annotations.util.RandomBeans.random(Destination.class); - String sqlStatement = "select * from nw.withdrawal_session LIMIT 1;"; + String sqlStatement = "select * from dw.withdrawal_session LIMIT 1;"; @BeforeEach public void setUp() { diff --git a/src/test/java/dev/vality/newway/kafka/KafkaProducer.java b/src/test/java/dev/vality/newway/kafka/KafkaProducer.java index 17d3729c..1b3ceb06 100644 --- a/src/test/java/dev/vality/newway/kafka/KafkaProducer.java +++ b/src/test/java/dev/vality/newway/kafka/KafkaProducer.java @@ -11,6 +11,7 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; @TestComponent @Import(KafkaProducerConfig.class) @@ -31,7 +32,7 @@ private MachineEvent createMessage() { MachineEvent message = new MachineEvent(); dev.vality.machinegun.msgpack.Value data = new dev.vality.machinegun.msgpack.Value(); data.setBin(new byte[0]); - message.setCreatedAt(LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME)); + message.setCreatedAt(LocalDateTime.now().truncatedTo(ChronoUnit.MICROS).format(DateTimeFormatter.ISO_DATE_TIME)); message.setEventId(1L); message.setSourceNs("sad"); message.setSourceId("sda"); diff --git a/src/test/java/dev/vality/newway/listener/InvoicingListenerTest.java b/src/test/java/dev/vality/newway/listener/InvoicingListenerTest.java index fedeee34..b3322aa9 100644 --- a/src/test/java/dev/vality/newway/listener/InvoicingListenerTest.java +++ b/src/test/java/dev/vality/newway/listener/InvoicingListenerTest.java @@ -9,9 +9,10 @@ import dev.vality.machinegun.eventsink.SinkEvent; import dev.vality.newway.exception.ParseException; import dev.vality.newway.mapper.invoice.InvoiceCreatedMapper; -import dev.vality.newway.service.InvoiceBatchService; +import dev.vality.newway.service.InvoiceWrapperService; import dev.vality.newway.service.InvoicingService; -import dev.vality.newway.service.PaymentBatchService; +import dev.vality.newway.service.PartyShopCacheService; +import dev.vality.newway.service.PaymentWrapperService; import dev.vality.newway.utils.MockUtils; import dev.vality.sink.common.parser.impl.MachineEventParser; import org.apache.kafka.clients.consumer.ConsumerRecord; @@ -25,6 +26,7 @@ import org.springframework.kafka.support.Acknowledgment; import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -35,9 +37,11 @@ public class InvoicingListenerTest { @Mock - private InvoiceBatchService invoiceBatchService; + private PartyShopCacheService partyShopCacheService; @Mock - private PaymentBatchService paymentBatchService; + private InvoiceWrapperService invoiceBatchService; + @Mock + private PaymentWrapperService paymentWrapperService; @Mock private MachineEventParser eventParser; @Mock @@ -47,9 +51,15 @@ public class InvoicingListenerTest { @BeforeEach public void init() { - listener = new InvoicingKafkaListener( - new InvoicingService(new ArrayList<>(), Collections.singletonList(new InvoiceCreatedMapper()), - new ArrayList<>(), invoiceBatchService, paymentBatchService, eventParser)); + listener = new InvoicingKafkaListener(new InvoicingService( + new ArrayList<>(), + Collections.singletonList(new InvoiceCreatedMapper()), + new ArrayList<>(), + partyShopCacheService, + invoiceBatchService, + paymentWrapperService, + eventParser + )); } @Test @@ -67,7 +77,7 @@ public void listenNonInvoiceChanges() { listener.handle(Collections.singletonList(new ConsumerRecord<>("topic", 1, 1, "kek", sinkEvent)), ack); - Mockito.verify(invoiceBatchService, Mockito.times(0)).process(anyList()); + Mockito.verify(invoiceBatchService, Mockito.times(0)).save(anyList()); Mockito.verify(ack, Mockito.times(1)).acknowledge(); } @@ -88,7 +98,7 @@ public void listenEmptyException() { @Test public void listenChanges() { MachineEvent message = new MachineEvent(); - message.setCreatedAt(TypeUtil.temporalToString(LocalDateTime.now())); + message.setCreatedAt(TypeUtil.temporalToString(LocalDateTime.now().truncatedTo(ChronoUnit.MICROS))); EventPayload payload = new EventPayload(); ArrayList invoiceChanges = new ArrayList<>(); InvoiceChange invoiceChange = new InvoiceChange(); @@ -105,7 +115,7 @@ public void listenChanges() { listener.handle(Collections.singletonList(new ConsumerRecord<>("topic", 1, 1, "kek", sinkEvent)), ack); - Mockito.verify(invoiceBatchService, Mockito.times(1)).process(anyList()); + Mockito.verify(invoiceBatchService, Mockito.times(1)).save(anyList()); Mockito.verify(ack, Mockito.times(1)).acknowledge(); } } diff --git a/src/test/java/dev/vality/newway/model/InvoiceWrapperTest.java b/src/test/java/dev/vality/newway/model/InvoiceWrapperTest.java deleted file mode 100644 index c097eabc..00000000 --- a/src/test/java/dev/vality/newway/model/InvoiceWrapperTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package dev.vality.newway.model; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class InvoiceWrapperTest { - - @Test - public void copyTest() { - InvoiceWrapper invoiceWrapper = dev.vality.testcontainers.annotations.util.RandomBeans.random(InvoiceWrapper.class); - InvoiceWrapper copy = invoiceWrapper.copy(); - invoiceWrapper.getInvoice().setInvoiceId("kek"); - invoiceWrapper.getCarts().get(0).setInvId(124L); - Assertions.assertNotEquals(invoiceWrapper.getInvoice().getInvoiceId(), copy.getInvoice().getInvoiceId()); - Assertions.assertNotEquals(invoiceWrapper.getCarts().get(0).getInvId(), copy.getCarts().get(0).getInvId()); - } -} diff --git a/src/test/java/dev/vality/newway/model/PaymentWrapperTest.java b/src/test/java/dev/vality/newway/model/PaymentWrapperTest.java deleted file mode 100644 index d86ddb64..00000000 --- a/src/test/java/dev/vality/newway/model/PaymentWrapperTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package dev.vality.newway.model; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class PaymentWrapperTest { - - @Test - public void copyTest() { - PaymentWrapper paymentWrapper = dev.vality.testcontainers.annotations.util.RandomBeans.random(PaymentWrapper.class); - PaymentWrapper copy = paymentWrapper.copy(); - paymentWrapper.getPayment().setInvoiceId("kek"); - paymentWrapper.getCashFlows().get(0).setObjId(124L); - Assertions.assertNotEquals(paymentWrapper.getPayment().getInvoiceId(), copy.getPayment().getInvoiceId()); - Assertions.assertNotEquals(paymentWrapper.getCashFlows().get(0).getObjId(), copy.getCashFlows().get(0).getObjId()); - } -} diff --git a/src/test/java/dev/vality/newway/service/CashFlowWrapperHandlerTest.java b/src/test/java/dev/vality/newway/service/CashFlowWrapperHandlerTest.java new file mode 100644 index 00000000..5b9fea00 --- /dev/null +++ b/src/test/java/dev/vality/newway/service/CashFlowWrapperHandlerTest.java @@ -0,0 +1,130 @@ +package dev.vality.newway.service; + +import dev.vality.newway.config.PostgresqlSpringBootITest; +import dev.vality.newway.dao.invoicing.iface.CashFlowDao; +import dev.vality.newway.dao.invoicing.iface.CashFlowLinkDao; +import dev.vality.newway.domain.enums.PaymentChangeType; +import dev.vality.newway.domain.tables.pojos.CashFlow; +import dev.vality.newway.domain.tables.pojos.CashFlowLink; +import dev.vality.newway.handler.wrapper.payment.CashFlowWrapperHandler; +import dev.vality.newway.model.CashFlowWrapper; +import dev.vality.newway.model.PaymentWrapper; +import dev.vality.newway.utils.MockUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static dev.vality.newway.utils.JdbcUtil.countEntities; +import static dev.vality.newway.utils.JdbcUtil.countPaymentEntity; +import static org.junit.jupiter.api.Assertions.*; + +@PostgresqlSpringBootITest +public class CashFlowWrapperHandlerTest { + + @Autowired + private CashFlowWrapperHandler service; + + @Autowired + private JdbcTemplate jdbcTemplate; + + @Autowired + private CashFlowDao cashFlowDao; + + @Autowired + private CashFlowLinkDao cashFlowLinkDao; + + private final String invoiceId = "invoiceId"; + private final String paymentId = "paymentId"; + + @BeforeEach + void setUp() { + jdbcTemplate.execute("truncate table dw.cash_flow_link cascade"); + jdbcTemplate.execute("truncate table dw.cash_flow cascade"); + } + + @Test + void saveTest() { + CashFlowWrapper first = MockUtils.buildCashFlowWrapper(invoiceId, paymentId, 1L, 1); + List wrappers = createPaymentWrappers(first); + + service.saveBatch(wrappers); + CashFlowLink cashFlowLink = cashFlowLinkDao.get(invoiceId, paymentId); + assertCashFlowLink(first.getCashFlowLink(), cashFlowLink); + + Map actualCashFlowsMap = cashFlowDao.getByObjId(cashFlowLink.getId(), PaymentChangeType.payment) + .stream() + .collect(Collectors.toMap(CashFlow::getAmount, cashFlow -> cashFlow)); + assertCashFlows(first.getCashFlows(), actualCashFlowsMap, cashFlowLink.getId()); + } + + @Test + public void duplicationTest() { + CashFlowWrapper first = MockUtils.buildCashFlowWrapper(invoiceId, paymentId, 1L, 1); + CashFlowWrapper second = MockUtils.buildCashFlowWrapper(invoiceId, paymentId, 2L, 1); + List wrappers = createPaymentWrappers(first, second); + + service.saveBatch(wrappers); + CashFlowLink currentLink = cashFlowLinkDao.get(invoiceId, paymentId); + assertCashFlowLink(second.getCashFlowLink(), currentLink); + Map actualCashFlowsMap = cashFlowDao.getByObjId(currentLink.getId(), PaymentChangeType.payment) + .stream() + .collect(Collectors.toMap(CashFlow::getAmount, cashFlow -> cashFlow)); + assertCashFlows(second.getCashFlows(), actualCashFlowsMap, currentLink.getId()); + + service.saveBatch(wrappers); + assertEquals(2, countPaymentEntity(jdbcTemplate, "cash_flow_link", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "cash_flow_link", invoiceId, paymentId, true)); + assertEquals(6, countEntities(jdbcTemplate, "cash_flow")); + } + + private void assertCashFlowLink(CashFlowLink expected, CashFlowLink actual) { + assertNotNull(actual.getId()); + assertNotNull(actual.getWtime()); + assertTrue(actual.getCurrent()); + assertEquals(expected.getEventCreatedAt(), actual.getEventCreatedAt()); + assertEquals(expected.getInvoiceId(), actual.getInvoiceId()); + assertEquals(expected.getPaymentId(), actual.getPaymentId()); + assertEquals(expected.getSequenceId(), actual.getSequenceId()); + assertEquals(expected.getChangeId(), actual.getChangeId()); + } + + private void assertCashFlows(List expectedCashFlows, + Map actualCashFlowsByAmount, + Long objId) { + assertEquals(expectedCashFlows.size(), actualCashFlowsByAmount.size()); + expectedCashFlows.forEach(expected -> { + CashFlow actual = actualCashFlowsByAmount.get(expected.getAmount()); + assertNotNull(actual.getId()); + assertNull(actual.getAdjFlowType()); + assertEquals(objId, actual.getObjId()); + assertEquals(expected.getObjType(), actual.getObjType()); + assertEquals(expected.getSourceAccountType(), actual.getSourceAccountType()); + assertEquals(expected.getSourceAccountTypeValue(), actual.getSourceAccountTypeValue()); + assertEquals(expected.getSourceAccountId(), actual.getSourceAccountId()); + assertEquals(expected.getDestinationAccountType(), actual.getDestinationAccountType()); + assertEquals(expected.getDestinationAccountTypeValue(), actual.getDestinationAccountTypeValue()); + assertEquals(expected.getDestinationAccountId(), actual.getDestinationAccountId()); + assertEquals(expected.getAmount(), actual.getAmount()); + assertEquals(expected.getCurrencyCode(), actual.getCurrencyCode()); + assertEquals(expected.getDetails(), actual.getDetails()); + }); + } + + private List createPaymentWrappers(CashFlowWrapper... wrappers) { + List paymentWrappers = new ArrayList<>(); + for (CashFlowWrapper cashFlowWrapper : wrappers) { + var paymentWrapper = new PaymentWrapper(); + paymentWrapper.setCashFlowWrapper(cashFlowWrapper); + paymentWrappers.add(paymentWrapper); + } + return paymentWrappers; + } + + +} diff --git a/src/test/java/dev/vality/newway/service/InvoiceBatchServiceTest.java b/src/test/java/dev/vality/newway/service/InvoiceBatchServiceTest.java deleted file mode 100644 index c6b3d213..00000000 --- a/src/test/java/dev/vality/newway/service/InvoiceBatchServiceTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package dev.vality.newway.service; - -import dev.vality.newway.config.PostgresqlSpringBootITest; -import dev.vality.newway.dao.invoicing.iface.InvoiceDao; -import dev.vality.newway.domain.tables.pojos.Invoice; -import dev.vality.newway.domain.tables.pojos.InvoiceCart; -import dev.vality.newway.model.InvoiceWrapper; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.JdbcTemplate; - -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -@PostgresqlSpringBootITest -public class InvoiceBatchServiceTest { - - @Autowired - private InvoiceBatchService invoiceBatchService; - - @Autowired - private InvoiceDao invoiceDao; - - @Autowired - private JdbcTemplate jdbcTemplate; - - @Test - public void processTest() { - List invoiceWrappers = IntStream.range(1, 5) - .mapToObj(x -> new InvoiceWrapper(dev.vality.testcontainers.annotations.util.RandomBeans.random(Invoice.class, "id"), - dev.vality.testcontainers.annotations.util.RandomBeans.randomListOf(3, InvoiceCart.class, "id", "invId"))) - .collect(Collectors.toList()); - - String invoiceIdFirst = "invoiceIdFirst"; - String invoiceIdSecond = "invoiceIdSecond"; - invoiceWrappers.get(0).getInvoice().setInvoiceId(invoiceIdFirst); - invoiceWrappers.get(1).getInvoice().setInvoiceId(invoiceIdFirst); - invoiceWrappers.get(2).getInvoice().setInvoiceId(invoiceIdSecond); - invoiceWrappers.get(3).getInvoice().setInvoiceId(invoiceIdSecond); - invoiceWrappers.forEach(iw -> iw.getInvoice().setCurrent(false)); - invoiceBatchService.process(invoiceWrappers); - - Invoice invoiceFirstGet = invoiceDao.get(invoiceIdFirst); - Assertions.assertNotEquals(invoiceWrappers.get(0).getInvoice().getPartyId(), invoiceFirstGet.getPartyId()); - Assertions.assertEquals(invoiceWrappers.get(1).getInvoice().getPartyId(), invoiceFirstGet.getPartyId()); - - Invoice invoiceSecondGet = invoiceDao.get(invoiceIdSecond); - Assertions.assertNotEquals(invoiceWrappers.get(2).getInvoice().getShopId(), invoiceSecondGet.getShopId()); - Assertions.assertEquals(invoiceWrappers.get(3).getInvoice().getShopId(), invoiceSecondGet.getShopId()); - - //Duplication check - invoiceBatchService.process(invoiceWrappers); - Assertions.assertEquals(2, Objects.requireNonNull(jdbcTemplate - .queryForObject("SELECT count(*) FROM nw.invoice WHERE invoice_id = ? ", new Object[]{invoiceIdFirst}, - Integer.class)).intValue()); - Assertions.assertEquals(2, Objects.requireNonNull(jdbcTemplate - .queryForObject("SELECT count(*) FROM nw.invoice WHERE invoice_id = ? ", new Object[]{invoiceIdSecond}, - Integer.class)).intValue()); - Assertions.assertEquals(3, Objects.requireNonNull(jdbcTemplate.queryForObject("SELECT count(*) FROM nw.invoice_cart where inv_id = ? ", - new Object[]{invoiceFirstGet.getId()}, Integer.class)).intValue()); - Assertions.assertEquals(24, Objects.requireNonNull(jdbcTemplate.queryForObject("SELECT count(*) FROM nw.invoice_cart ", Integer.class)).intValue()); - } -} diff --git a/src/test/java/dev/vality/newway/service/InvoiceWrapperServiceTest.java b/src/test/java/dev/vality/newway/service/InvoiceWrapperServiceTest.java index dade2269..e439cb19 100644 --- a/src/test/java/dev/vality/newway/service/InvoiceWrapperServiceTest.java +++ b/src/test/java/dev/vality/newway/service/InvoiceWrapperServiceTest.java @@ -1,38 +1,117 @@ package dev.vality.newway.service; import dev.vality.newway.config.PostgresqlSpringBootITest; +import dev.vality.newway.dao.invoicing.iface.InvoiceCartDao; +import dev.vality.newway.dao.invoicing.iface.InvoiceDao; +import dev.vality.newway.dao.invoicing.iface.InvoiceStatusInfoDao; import dev.vality.newway.domain.tables.pojos.Invoice; import dev.vality.newway.domain.tables.pojos.InvoiceCart; -import dev.vality.newway.handler.event.stock.LocalStorage; +import dev.vality.newway.domain.tables.pojos.InvoiceStatusInfo; import dev.vality.newway.model.InvoiceWrapper; -import org.junit.jupiter.api.Assertions; +import dev.vality.testcontainers.annotations.util.RandomBeans; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; +import static dev.vality.newway.utils.JdbcUtil.countEntities; +import static dev.vality.newway.utils.JdbcUtil.countInvoiceEntity; +import static org.junit.jupiter.api.Assertions.*; + @PostgresqlSpringBootITest public class InvoiceWrapperServiceTest { @Autowired - private InvoiceWrapperService service; + private InvoiceWrapperService invoiceWrapperService; + + @Autowired + private InvoiceDao invoiceDao; + + @Autowired + private InvoiceStatusInfoDao invoiceStatusInfoDao; + + @Autowired + private InvoiceCartDao invoiceCartDao; + + @Autowired + private JdbcTemplate jdbcTemplate; @Test - public void testGet() { + public void processTest() { + List invoiceWrappers = prepareInvoiceWrappers(); + invoiceWrapperService.save(invoiceWrappers); + invoiceWrappers.forEach(this::assertInvoiceWrapper); + } + + @Test + public void duplicationTest() { + List invoiceWrappers = prepareInvoiceWrappers(); + invoiceWrapperService.save(invoiceWrappers); + + invoiceWrapperService.save(invoiceWrappers); + invoiceWrappers.forEach(wrapper -> assertDuplication(wrapper.getInvoice().getInvoiceId())); + assertTotal(); + } + + private List prepareInvoiceWrappers() { List invoiceWrappers = IntStream.range(1, 5) - .mapToObj(x -> new InvoiceWrapper(dev.vality.testcontainers.annotations.util.RandomBeans.random(Invoice.class), dev.vality.testcontainers.annotations.util.RandomBeans.randomListOf(3, InvoiceCart.class))) + .mapToObj(x -> new InvoiceWrapper( + RandomBeans.random(Invoice.class, "id"), + RandomBeans.random(InvoiceStatusInfo.class, "id", "invoiceId"), + RandomBeans.randomListOf(3, InvoiceCart.class, "id", "invoiceId"))) .collect(Collectors.toList()); invoiceWrappers.forEach(iw -> { - iw.getInvoice().setCurrent(false); - iw.getCarts().forEach(c -> c.setInvId(iw.getInvoice().getId())); + iw.getInvoiceStatusInfo().setInvoiceId(iw.getInvoice().getInvoiceId()); + iw.getCarts().forEach(cart -> + cart.setInvoiceId(iw.getInvoice().getInvoiceId())); }); - service.save(invoiceWrappers); + return invoiceWrappers; + } + + private void assertInvoiceWrapper(InvoiceWrapper invoiceWrapper) { + assertInvoice(invoiceWrapper.getInvoice()); + assertInvoiceStatus(invoiceWrapper.getInvoiceStatusInfo()); + assertInvoiceCart(invoiceWrapper.getCarts()); + } + + private void assertInvoice(Invoice expected) { + Invoice actual = invoiceDao.get(expected.getInvoiceId()); + assertNotNull(actual.getId()); + actual.setId(null); + assertEquals(expected, actual); + } + + private void assertInvoiceStatus(InvoiceStatusInfo expected) { + InvoiceStatusInfo actual = invoiceStatusInfoDao.get(expected.getInvoiceId()); + assertNotNull(actual.getId()); + actual.setId(null); + assertTrue(actual.getCurrent()); + actual.setCurrent(expected.getCurrent()); + assertEquals(expected, actual); + } + + private void assertInvoiceCart(List expected) { + List actual = invoiceCartDao.getByInvoiceId(expected.get(0).getInvoiceId()); + actual.forEach(cart -> { + assertNotNull(cart.getId()); + cart.setId(null); + }); + assertEquals(expected, actual); + } + + private void assertDuplication(String invoiceId) { + assertEquals(1, countInvoiceEntity(jdbcTemplate, "invoice", invoiceId, false)); + assertEquals(1, countInvoiceEntity(jdbcTemplate, "invoice_status_info", invoiceId, false)); + assertEquals(3, countInvoiceEntity(jdbcTemplate, "invoice_cart", invoiceId, false)); + } - InvoiceWrapper invoiceWrapper = service.get(invoiceWrappers.get(0).getInvoice().getInvoiceId(), - new LocalStorage()); - Assertions.assertEquals(invoiceWrappers.get(0).getInvoice().getShopId(), invoiceWrapper.getInvoice().getShopId()); + private void assertTotal() { + assertEquals(4, countEntities(jdbcTemplate, "invoice")); + assertEquals(4, countEntities(jdbcTemplate, "invoice_status_info")); + assertEquals(12, countEntities(jdbcTemplate, "invoice_cart")); } } diff --git a/src/test/java/dev/vality/newway/service/InvoicingServiceTest.java b/src/test/java/dev/vality/newway/service/InvoicingServiceTest.java index 8e2f439d..ae2fe0a0 100644 --- a/src/test/java/dev/vality/newway/service/InvoicingServiceTest.java +++ b/src/test/java/dev/vality/newway/service/InvoicingServiceTest.java @@ -11,17 +11,16 @@ import dev.vality.newway.domain.enums.PaymentChangeType; import dev.vality.newway.domain.tables.pojos.Chargeback; import dev.vality.newway.domain.tables.pojos.Payment; -import dev.vality.newway.factory.MachineEventCopyFactory; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; import dev.vality.newway.handler.event.stock.impl.invoicing.InvoicingHandler; import dev.vality.newway.handler.event.stock.impl.invoicing.chargeback.*; -import dev.vality.newway.mapper.AbstractInvoicingMapper; +import dev.vality.newway.mapper.Mapper; import dev.vality.newway.model.InvoiceWrapper; import dev.vality.sink.common.parser.impl.MachineEventParser; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -33,13 +32,15 @@ @ExtendWith(SpringExtension.class) public class InvoicingServiceTest { - private final List> wrongHandlers = new ArrayList<>(); - private final List> rightHandlers = new ArrayList<>(); + private final List> wrongHandlers = new ArrayList<>(); + private final List> rightHandlers = new ArrayList<>(); @MockBean - private InvoiceBatchService invoiceBatchService; + private InvoiceWrapperService invoiceWrapperService; @MockBean - private PaymentBatchService paymentBatchService; + private PaymentWrapperService paymentWrapperService; + @MockBean + private PartyShopCacheService partyShopCacheService; @Mock private MachineEventCopyFactory machineEventCopyFactory; @Mock @@ -51,11 +52,11 @@ public class InvoicingServiceTest { @BeforeEach public void init() { - AbstractInvoicingMapper wrong = mock(AbstractInvoicingMapper.class); + Mapper wrong = mock(Mapper.class); when(wrong.accept(any())).thenReturn(false); wrongHandlers.add(wrong); - AbstractInvoicingMapper right = mock(AbstractInvoicingMapper.class); + Mapper right = mock(Mapper.class); when(right.accept(any())).thenReturn(true); rightHandlers.add(right); @@ -67,9 +68,15 @@ public void init() { @Test public void handleEmptyChanges() { - InvoicingService invoicingService = - new InvoicingService(new ArrayList<>(), rightHandlers, new ArrayList<>(), invoiceBatchService, - paymentBatchService, parser); + InvoicingService invoicingService = new InvoicingService( + new ArrayList<>(), + rightHandlers, + new ArrayList<>(), + partyShopCacheService, + invoiceWrapperService, + paymentWrapperService, + parser + ); EventPayload eventPayload = new EventPayload(); when(parser.parse(any())).thenReturn(eventPayload); @@ -81,9 +88,15 @@ public void handleEmptyChanges() { @Test public void handlerSupportsInvoicing() { - InvoicingService invoicingService = - new InvoicingService(new ArrayList<>(), rightHandlers, new ArrayList<>(), invoiceBatchService, - paymentBatchService, parser); + InvoicingService invoicingService = new InvoicingService( + new ArrayList<>(), + rightHandlers, + new ArrayList<>(), + partyShopCacheService, + invoiceWrapperService, + paymentWrapperService, + parser + ); MachineEvent message = new MachineEvent(); @@ -94,14 +107,20 @@ public void handlerSupportsInvoicing() { invoicingService.handleEvents(Collections.singletonList(message)); verify(rightHandlers.get(0), times(1)).accept(any()); - verify(rightHandlers.get(0), times(1)).map(any(), any(), any(), any()); + verify(rightHandlers.get(0), times(1)).map(any(), any(), any()); } @Test public void handlerNotSupportInvoicing() { - InvoicingService invoicingService = - new InvoicingService(new ArrayList<>(), wrongHandlers, new ArrayList<>(), invoiceBatchService, - paymentBatchService, parser); + InvoicingService invoicingService = new InvoicingService( + new ArrayList<>(), + wrongHandlers, + new ArrayList<>(), + partyShopCacheService, + invoiceWrapperService, + paymentWrapperService, + parser + ); EventPayload eventPayload = new EventPayload(); eventPayload.setInvoiceChanges(Collections.singletonList(new InvoiceChange())); @@ -110,7 +129,7 @@ public void handlerNotSupportInvoicing() { invoicingService.handleEvents(Collections.singletonList(new MachineEvent())); verify(wrongHandlers.get(0), times(1)).accept(any()); - verify(wrongHandlers.get(0), times(0)).map(any(), any(), any(), any()); + verify(wrongHandlers.get(0), times(0)).map(any(), any(), any()); } @Test @@ -124,13 +143,18 @@ public void handlerInvoicePaymentChargebackCreated() { when(parser.parse(any())).thenReturn(eventPayload); List handlers = chargebackHandlers(chargebackDao, cashFlowDao, paymentDao); - InvoicingService invoicingService = - new InvoicingService(handlers, wrongHandlers, new ArrayList<>(), invoiceBatchService, - paymentBatchService, parser); + InvoicingService invoicingService = new InvoicingService( + handlers, + wrongHandlers, + new ArrayList<>(), + partyShopCacheService, + invoiceWrapperService, + paymentWrapperService, + parser); MachineEvent machineEvent = buildMachineEvent(); invoicingService.handleEvents(Collections.singletonList(machineEvent)); - Mockito.verify(chargebackDao, only()).save(any(Chargeback.class)); + verify(chargebackDao, only()).save(any(Chargeback.class)); } @Test @@ -144,16 +168,21 @@ public void handlerInvoicePaymentChargebackStatusChanged() { when(parser.parse(any())).thenReturn(eventPayload); List handlers = chargebackHandlers(chargebackDao, cashFlowDao, paymentDao); - InvoicingService invoicingService = - new InvoicingService(handlers, wrongHandlers, new ArrayList<>(), invoiceBatchService, - paymentBatchService, parser); + InvoicingService invoicingService = new InvoicingService( + handlers, + wrongHandlers, + new ArrayList<>(), + partyShopCacheService, + invoiceWrapperService, + paymentWrapperService, + parser); MachineEvent machineEvent = buildMachineEvent(); invoicingService.handleEvents(Collections.singletonList(machineEvent)); - Mockito.verify(chargebackDao, times(1)).save(any(Chargeback.class)); - Mockito.verify(chargebackDao, times(1)).updateNotCurrent(anyLong()); - Mockito.verify(cashFlowService, times(1)).save(anyLong(), anyLong(), any(PaymentChangeType.class)); + verify(chargebackDao, times(1)).save(any(Chargeback.class)); + verify(chargebackDao, times(1)).updateNotCurrent(anyLong()); + verify(cashFlowService, times(1)).save(anyLong(), anyLong(), any(PaymentChangeType.class)); } @Test @@ -167,16 +196,21 @@ public void handlerInvoicePaymentChargebackLevyChanged() { when(parser.parse(any())).thenReturn(eventPayload); List handlers = chargebackHandlers(chargebackDao, cashFlowDao, paymentDao); - InvoicingService invoicingService = - new InvoicingService(handlers, wrongHandlers, new ArrayList<>(), invoiceBatchService, - paymentBatchService, parser); + InvoicingService invoicingService = new InvoicingService( + handlers, + wrongHandlers, + new ArrayList<>(), + partyShopCacheService, + invoiceWrapperService, + paymentWrapperService, + parser); MachineEvent machineEvent = buildMachineEvent(); invoicingService.handleEvents(Collections.singletonList(machineEvent)); - Mockito.verify(chargebackDao, times(1)).save(any(Chargeback.class)); - Mockito.verify(chargebackDao, times(1)).updateNotCurrent(anyLong()); - Mockito.verify(cashFlowService, times(1)).save(anyLong(), anyLong(), any(PaymentChangeType.class)); + verify(chargebackDao, times(1)).save(any(Chargeback.class)); + verify(chargebackDao, times(1)).updateNotCurrent(anyLong()); + verify(cashFlowService, times(1)).save(anyLong(), anyLong(), any(PaymentChangeType.class)); } @Test @@ -189,16 +223,21 @@ public void handlerInvoicePaymentChargebackStageChanged() { eventPayload.setInvoiceChanges(Collections.singletonList(invoiceChange)); when(parser.parse(any())).thenReturn(eventPayload); List handlers = chargebackHandlers(chargebackDao, cashFlowDao, paymentDao); - InvoicingService invoicingService = - new InvoicingService(handlers, wrongHandlers, new ArrayList<>(), invoiceBatchService, - paymentBatchService, parser); + InvoicingService invoicingService = new InvoicingService( + handlers, + wrongHandlers, + new ArrayList<>(), + partyShopCacheService, + invoiceWrapperService, + paymentWrapperService, + parser); MachineEvent machineEvent = buildMachineEvent(); invoicingService.handleEvents(Collections.singletonList(machineEvent)); - Mockito.verify(chargebackDao, times(1)).save(any(Chargeback.class)); - Mockito.verify(chargebackDao, times(1)).updateNotCurrent(anyLong()); - Mockito.verify(cashFlowService, times(1)).save(anyLong(), anyLong(), any(PaymentChangeType.class)); + verify(chargebackDao, times(1)).save(any(Chargeback.class)); + verify(chargebackDao, times(1)).updateNotCurrent(anyLong()); + verify(cashFlowService, times(1)).save(anyLong(), anyLong(), any(PaymentChangeType.class)); } @Test @@ -211,17 +250,22 @@ public void handlerInvoicePaymentChargebackCashFlowChanged() { eventPayload.setInvoiceChanges(Collections.singletonList(invoiceChange)); when(parser.parse(any())).thenReturn(eventPayload); List handlers = chargebackHandlers(chargebackDao, cashFlowDao, paymentDao); - InvoicingService invoicingService = - new InvoicingService(handlers, wrongHandlers, new ArrayList<>(), invoiceBatchService, - paymentBatchService, parser); + InvoicingService invoicingService = new InvoicingService( + handlers, + wrongHandlers, + new ArrayList<>(), + partyShopCacheService, + invoiceWrapperService, + paymentWrapperService, + parser); MachineEvent machineEvent = buildMachineEvent(); invoicingService.handleEvents(Collections.singletonList(machineEvent)); - Mockito.verify(chargebackDao, times(1)).save(any(Chargeback.class)); - Mockito.verify(chargebackDao, times(1)).updateNotCurrent(anyLong()); - Mockito.verify(cashFlowService, times(1)).save(anyLong(), anyLong(), any(PaymentChangeType.class)); - Mockito.verify(cashFlowDao, times(1)).save(anyList()); + verify(chargebackDao, times(1)).save(any(Chargeback.class)); + verify(chargebackDao, times(1)).updateNotCurrent(anyLong()); + verify(cashFlowService, times(1)).save(anyLong(), anyLong(), any(PaymentChangeType.class)); + verify(cashFlowDao, times(1)).save(anyList()); } @Test @@ -234,16 +278,21 @@ public void handlerInvoicePaymentChargebackBodyChanged() { eventPayload.setInvoiceChanges(Collections.singletonList(invoiceChange)); when(parser.parse(any())).thenReturn(eventPayload); List handlers = chargebackHandlers(chargebackDao, cashFlowDao, paymentDao); - InvoicingService invoicingService = - new InvoicingService(handlers, wrongHandlers, new ArrayList<>(), invoiceBatchService, - paymentBatchService, parser); + InvoicingService invoicingService = new InvoicingService( + handlers, + wrongHandlers, + new ArrayList<>(), + partyShopCacheService, + invoiceWrapperService, + paymentWrapperService, + parser); MachineEvent machineEvent = buildMachineEvent(); invoicingService.handleEvents(Collections.singletonList(machineEvent)); - Mockito.verify(chargebackDao, times(1)).save(any(Chargeback.class)); - Mockito.verify(chargebackDao, times(1)).updateNotCurrent(anyLong()); - Mockito.verify(cashFlowService, times(1)).save(anyLong(), anyLong(), any(PaymentChangeType.class)); + verify(chargebackDao, times(1)).save(any(Chargeback.class)); + verify(chargebackDao, times(1)).updateNotCurrent(anyLong()); + verify(cashFlowService, times(1)).save(anyLong(), anyLong(), any(PaymentChangeType.class)); } private ChargebackDao mockChargebackDao() { diff --git a/src/test/java/dev/vality/newway/service/PartyManagementServiceTest.java b/src/test/java/dev/vality/newway/service/PartyManagementServiceTest.java index 3e437ed6..85d4b4e2 100644 --- a/src/test/java/dev/vality/newway/service/PartyManagementServiceTest.java +++ b/src/test/java/dev/vality/newway/service/PartyManagementServiceTest.java @@ -8,7 +8,7 @@ import dev.vality.machinegun.msgpack.Value; import dev.vality.newway.config.SerializationConfig; import dev.vality.newway.dao.party.iface.PartyDao; -import dev.vality.newway.factory.PartyMachineEventCopyFactoryImpl; +import dev.vality.newway.factory.machine.event.PartyMachineEventCopyFactoryImpl; import dev.vality.newway.handler.event.stock.impl.partymngmnt.party.PartyCreatedHandler; import dev.vality.sink.common.serialization.impl.PartyEventDataSerializer; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/dev/vality/newway/service/PaymentBatchServiceTest.java b/src/test/java/dev/vality/newway/service/PaymentBatchServiceTest.java deleted file mode 100644 index c7aff606..00000000 --- a/src/test/java/dev/vality/newway/service/PaymentBatchServiceTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package dev.vality.newway.service; - -import dev.vality.newway.config.PostgresqlSpringBootITest; -import dev.vality.newway.dao.invoicing.impl.PaymentDaoImpl; -import dev.vality.newway.domain.enums.PaymentChangeType; -import dev.vality.newway.domain.tables.pojos.CashFlow; -import dev.vality.newway.domain.tables.pojos.Payment; -import dev.vality.newway.model.InvoicingKey; -import dev.vality.newway.model.PaymentWrapper; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.JdbcTemplate; - -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -@PostgresqlSpringBootITest -public class PaymentBatchServiceTest { - - @Autowired - private PaymentBatchService paymentBatchService; - - @Autowired - private PaymentDaoImpl paymentDao; - - @Autowired - private JdbcTemplate jdbcTemplate; - - @Test - public void processTest() { - List paymentWrappers = IntStream.range(1, 5) - .mapToObj(x -> new PaymentWrapper( - dev.vality.testcontainers.annotations.util.RandomBeans.random(Payment.class, "id"), - dev.vality.testcontainers.annotations.util.RandomBeans.randomListOf(3, CashFlow.class, "id", "objId"), - true, - null)) - .collect(Collectors.toList()); - - String invoiceIdFirst = "invoiceIdFirst"; - String invoiceIdSecond = "invoiceIdSecond"; - paymentWrappers.get(0).getPayment().setInvoiceId(invoiceIdFirst); - paymentWrappers.get(0).getPayment().setPaymentId("1"); - paymentWrappers.get(1).getPayment().setInvoiceId(invoiceIdFirst); - paymentWrappers.get(1).getPayment().setPaymentId("1"); - paymentWrappers.get(2).getPayment().setInvoiceId(invoiceIdFirst); - paymentWrappers.get(2).getPayment().setPaymentId("2"); - paymentWrappers.get(3).getPayment().setInvoiceId(invoiceIdSecond); - paymentWrappers.get(3).getPayment().setPaymentId("1"); - paymentWrappers.forEach(iw -> { - iw.setKey(InvoicingKey.buildKey(iw)); - iw.getPayment().setCurrent(false); - iw.getCashFlows().forEach(c -> c.setObjType(PaymentChangeType.payment)); - }); - paymentBatchService.process(paymentWrappers); - - Payment paymentFirstGet = paymentDao.get(invoiceIdFirst, "1"); - Assertions.assertNotEquals(paymentWrappers.get(0).getPayment().getPartyId(), paymentFirstGet.getPartyId()); - Assertions.assertEquals(paymentWrappers.get(1).getPayment().getPartyId(), paymentFirstGet.getPartyId()); - - Payment paymentSecondGet = paymentDao.get(invoiceIdFirst, "2"); - Assertions.assertEquals(paymentWrappers.get(2).getPayment().getShopId(), paymentSecondGet.getShopId()); - - //Duplication check - paymentBatchService.process(paymentWrappers); - Payment paymentFirstGet2 = paymentDao.get(invoiceIdFirst, "1"); - Assertions.assertEquals(paymentWrappers.get(1).getPayment().getPartyId(), paymentFirstGet2.getPartyId()); - Assertions.assertEquals(2, Objects.requireNonNull(jdbcTemplate - .queryForObject("SELECT count(*) FROM nw.payment WHERE invoice_id = ? and payment_id = ? ", - new Object[]{invoiceIdFirst, "1"}, Integer.class)).intValue()); - Assertions.assertEquals(1, Objects.requireNonNull(jdbcTemplate - .queryForObject("SELECT count(*) FROM nw.payment WHERE invoice_id = ? and payment_id = ? ", - new Object[]{invoiceIdFirst, "2"}, Integer.class)).intValue()); - Assertions.assertEquals(1, Objects.requireNonNull(jdbcTemplate - .queryForObject("SELECT count(*) FROM nw.payment WHERE invoice_id = ? and payment_id = ? ", - new Object[]{invoiceIdSecond, "1"}, Integer.class)).intValue()); - Assertions.assertEquals(24, Objects.requireNonNull(jdbcTemplate.queryForObject("SELECT count(*) FROM nw.cash_flow ", Integer.class)).intValue()); - } -} diff --git a/src/test/java/dev/vality/newway/service/PaymentSquashServiceTest.java b/src/test/java/dev/vality/newway/service/PaymentSquashServiceTest.java deleted file mode 100644 index ac396075..00000000 --- a/src/test/java/dev/vality/newway/service/PaymentSquashServiceTest.java +++ /dev/null @@ -1,176 +0,0 @@ -package dev.vality.newway.service; - -import dev.vality.newway.domain.tables.pojos.CashFlow; -import dev.vality.newway.domain.tables.pojos.Payment; -import dev.vality.newway.model.InvoicingKey; -import dev.vality.newway.model.PaymentWrapper; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.util.List; - -public class PaymentSquashServiceTest { - - @Test - public void squashSimpleTest() { - PaymentSquashService service = new PaymentSquashService(); - PaymentWrapper paymentWrapper = buildPaymentWrapper("1", 666L, true); - List squashedWrappers = service.squash(List.of(paymentWrapper), List.of(1L)); - Assertions.assertEquals(1, squashedWrappers.size()); - Assertions.assertTrue(squashedWrappers.get(0).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(0).getPayment().getId().longValue(), 1); - } - - @Test - public void squashSimple0Test() { - PaymentSquashService service = new PaymentSquashService(); - PaymentWrapper paymentWrapper = buildPaymentWrapper("1", 666L, false); - List squashedWrappers = service.squash(List.of(paymentWrapper), List.of(1L)); - Assertions.assertEquals(1, squashedWrappers.size()); - Assertions.assertFalse(squashedWrappers.get(0).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(0).getPayment().getId().longValue(), 666); - } - - @Test - public void squashSimple1Test() { - PaymentSquashService service = new PaymentSquashService(); - PaymentWrapper pw1 = buildPaymentWrapper("1", 666L, true); - PaymentWrapper pw2 = buildPaymentWrapper("inv_id", "1", 667L, 2L, false); - List squashedWrappers = service.squash(List.of(pw1, pw2), List.of(1L, 2L)); - Assertions.assertEquals(1, squashedWrappers.size()); - Assertions.assertTrue(squashedWrappers.get(0).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(0).getPayment().getId().longValue(), 1); - Assertions.assertEquals(squashedWrappers.get(0).getPayment().getPartyRevision(), pw2.getPayment().getPartyRevision()); - } - - @Test - public void squashSimple11Test() { - PaymentSquashService service = new PaymentSquashService(); - PaymentWrapper pw1 = buildPaymentWrapper("1", 666L, false); - PaymentWrapper pw2 = buildPaymentWrapper("inv_id", "1", 667L, 2L, false); - List squashedWrappers = service.squash(List.of(pw1, pw2), List.of(1L, 2L)); - Assertions.assertEquals(1, squashedWrappers.size()); - Assertions.assertFalse(squashedWrappers.get(0).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(0).getPayment().getId().longValue(), 666L); - Assertions.assertEquals(squashedWrappers.get(0).getPayment().getPartyRevision(), pw2.getPayment().getPartyRevision()); - } - - @Test - public void squashSimple12Test() { - PaymentSquashService service = new PaymentSquashService(); - PaymentWrapper pw1 = buildPaymentWrapper("1", 666L, true); - PaymentWrapper pw2 = buildPaymentWrapper("inv_id", "1", 667L, 2L, true); - List squashedWrappers = service.squash(List.of(pw1, pw2), List.of(1L, 2L)); - Assertions.assertEquals(2, squashedWrappers.size()); - Assertions.assertTrue(squashedWrappers.get(0).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(0).getPayment().getId().longValue(), 1); - Assertions.assertEquals(squashedWrappers.get(0).getPayment().getPartyRevision().longValue(), 0); - Assertions.assertTrue(squashedWrappers.get(1).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(1).getPayment().getId().longValue(), 2); - Assertions.assertEquals(squashedWrappers.get(1).getPayment().getPartyRevision(), pw2.getPayment().getPartyRevision()); - } - - @Test - public void squashSimple2Test() { - PaymentSquashService service = new PaymentSquashService(); - PaymentWrapper pw1 = buildPaymentWrapper("1", 666L, false); - PaymentWrapper pw2 = buildPaymentWrapper("inv_id", "1", 667L, 3L, true); - List squashedWrappers = service.squash(List.of(pw1, pw2), List.of(1L, 2L)); - Assertions.assertEquals(2, squashedWrappers.size()); - Assertions.assertFalse(squashedWrappers.get(0).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(0).getPayment().getId().longValue(), 666); - Assertions.assertEquals(squashedWrappers.get(0).getPayment().getPartyRevision().longValue(), 0); - Assertions.assertTrue(squashedWrappers.get(1).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(1).getPayment().getId().longValue(), 1); - Assertions.assertEquals(squashedWrappers.get(1).getPayment().getPartyRevision().longValue(), 3L); - } - - @Test - public void squashSimple3Test() { - PaymentSquashService service = new PaymentSquashService(); - PaymentWrapper pw1 = buildPaymentWrapper("1", 666L, true); - PaymentWrapper pw2 = buildPaymentWrapper("2", 667L, false); - List squashedWrappers = service.squash(List.of(pw1, pw2), List.of(1L, 2L)); - Assertions.assertEquals(2, squashedWrappers.size()); - Assertions.assertTrue(squashedWrappers.get(0).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(0).getPayment().getId().longValue(), 1); - Assertions.assertFalse(squashedWrappers.get(1).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(1).getPayment().getId().longValue(), 667); - } - - @Test - public void squashSimple4Test() { - PaymentSquashService service = new PaymentSquashService(); - PaymentWrapper pw1 = buildPaymentWrapper("1", 666L, true); - PaymentWrapper pw2 = buildPaymentWrapper("1", 667L, false); - PaymentWrapper pw3 = buildPaymentWrapper("1", 668L, false); - PaymentWrapper pw4 = buildPaymentWrapper("1", 669L, true); - PaymentWrapper pw5 = buildPaymentWrapper("1", 670L, false); - List squashedWrappers = - service.squash(List.of(pw1, pw2, pw3, pw4, pw5), List.of(1L, 2L, 3L, 4L, 5L)); - Assertions.assertEquals(2, squashedWrappers.size()); - Assertions.assertTrue(squashedWrappers.get(0).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(0).getPayment().getId().longValue(), 1); - Assertions.assertTrue(squashedWrappers.get(1).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(1).getPayment().getId().longValue(), 2); - } - - @Test - public void squashSimple41Test() { - PaymentSquashService service = new PaymentSquashService(); - PaymentWrapper pw1 = buildPaymentWrapper("1", 666L, true); - PaymentWrapper pw2 = buildPaymentWrapper("1", 667L, false); - PaymentWrapper pw3 = buildPaymentWrapper("2", 668L, false); - PaymentWrapper pw4 = buildPaymentWrapper("2", 669L, true); - PaymentWrapper pw5 = buildPaymentWrapper("inv_id", "2", 670L, 2L, false); - List squashedWrappers = - service.squash(List.of(pw1, pw2, pw3, pw4, pw5), List.of(1L, 2L, 3L, 4L, 5L)); - Assertions.assertEquals(3, squashedWrappers.size()); - Assertions.assertTrue(squashedWrappers.get(0).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(0).getPayment().getId().longValue(), 1); - Assertions.assertFalse(squashedWrappers.get(1).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(1).getPayment().getId().longValue(), 668); - Assertions.assertTrue(squashedWrappers.get(2).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(2).getPayment().getId().longValue(), 2); - Assertions.assertEquals(squashedWrappers.get(2).getPayment().getPaymentId(), "2"); - Assertions.assertEquals(squashedWrappers.get(2).getPayment().getPartyRevision(), pw5.getPayment().getPartyRevision()); - - } - - - @Test - public void squashSimple42Test() { - PaymentSquashService service = new PaymentSquashService(); - PaymentWrapper pw1 = buildPaymentWrapper("1", 666L, true); - PaymentWrapper pw2 = buildPaymentWrapper("1", 667L, false); - PaymentWrapper pw3 = buildPaymentWrapper("2", 668L, true); - PaymentWrapper pw4 = buildPaymentWrapper("2", 669L, false); - PaymentWrapper pw5 = buildPaymentWrapper("1", 670L, false); - List squashedWrappers = - service.squash(List.of(pw1, pw2, pw3, pw4, pw5), List.of(1L, 2L, 3L, 4L, 5L)); - Assertions.assertEquals(2, squashedWrappers.size()); - Assertions.assertTrue(squashedWrappers.get(0).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(0).getPayment().getId().longValue(), 1); - Assertions.assertTrue(squashedWrappers.get(1).isShouldInsert()); - Assertions.assertEquals(squashedWrappers.get(1).getPayment().getId().longValue(), 2); - } - - private PaymentWrapper buildPaymentWrapper(String invoiceId, String paymentId, Long id, Long partyRevision, - boolean isShouldInsert) { - Payment payment = new Payment(); - payment.setId(id); - payment.setInvoiceId(invoiceId); - payment.setPaymentId(paymentId); - payment.setPartyRevision(partyRevision); - PaymentWrapper paymentWrapper = new PaymentWrapper(); - paymentWrapper.setPayment(payment); - paymentWrapper.setCashFlows(List.of(new CashFlow())); - paymentWrapper.setKey(InvoicingKey.buildKey(invoiceId, paymentId)); - paymentWrapper.setShouldInsert(isShouldInsert); - return paymentWrapper; - } - - private PaymentWrapper buildPaymentWrapper(String paymentId, Long id, boolean isShouldInsert) { - return buildPaymentWrapper("inv_id", paymentId, id, 0L, isShouldInsert); - } -} diff --git a/src/test/java/dev/vality/newway/service/PaymentWrapperServiceTest.java b/src/test/java/dev/vality/newway/service/PaymentWrapperServiceTest.java new file mode 100644 index 00000000..0a3d1046 --- /dev/null +++ b/src/test/java/dev/vality/newway/service/PaymentWrapperServiceTest.java @@ -0,0 +1,134 @@ +package dev.vality.newway.service; + +import dev.vality.newway.config.PostgresqlSpringBootITest; +import dev.vality.newway.dao.invoicing.iface.*; +import dev.vality.newway.dao.invoicing.impl.PaymentDaoImpl; +import dev.vality.newway.domain.enums.PaymentChangeType; +import dev.vality.newway.domain.tables.pojos.*; +import dev.vality.newway.model.CashFlowWrapper; +import dev.vality.newway.model.PaymentWrapper; +import dev.vality.newway.utils.PaymentWrapperTestUtil; +import dev.vality.testcontainers.annotations.util.RandomBeans; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.util.HashSet; +import java.util.List; + +import static dev.vality.newway.utils.JdbcUtil.countPaymentEntity; +import static dev.vality.newway.utils.JdbcUtil.countEntities; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@PostgresqlSpringBootITest +public class PaymentWrapperServiceTest { + + @Autowired + private PaymentWrapperService paymentWrapperService; + + @Autowired + private PaymentDaoImpl paymentDao; + @Autowired + private PaymentStatusInfoDao paymentStatusInfoDao; + @Autowired + private PaymentPayerInfoDao paymentPayerInfoDao; + @Autowired + private PaymentAdditionalInfoDao paymentAdditionalInfoDao; + @Autowired + private PaymentRecurrentInfoDao paymentRecurrentInfoDao; + @Autowired + private PaymentRiskDataDao paymentRiskDataDao; + @Autowired + private PaymentFeeDao paymentFeeDao; + @Autowired + private PaymentRouteDao paymentRouteDao; + @Autowired + private CashFlowLinkDao cashFlowLinkDao; + @Autowired + private CashFlowDao cashFlowDao; + + @Autowired + private JdbcTemplate jdbcTemplate; + + private static final String invoiceIdFirst = "invoiceIdFirst"; + private static final String invoiceIdSecond = "invoiceIdSecond"; + private static final String paymentIdFirst = "paymentIdFirst"; + private static final String paymentIdSecond = "paymentIdSecond"; + + @Test + public void processTest() { + List paymentWrappers = preparePaymentWrappers(); + + paymentWrapperService.save(paymentWrappers); + assertPaymentWrapperFromDao(paymentWrappers.get(0), invoiceIdFirst, paymentIdFirst); + assertPaymentWrapperFromDao(paymentWrappers.get(1), invoiceIdSecond, paymentIdSecond); + } + + @Test + public void duplicationTest() { + List paymentWrappers = preparePaymentWrappers(); + + paymentWrapperService.save(paymentWrappers); + + paymentWrapperService.save(paymentWrappers); + assertDuplication(invoiceIdFirst, paymentIdFirst); + assertDuplication(invoiceIdSecond, paymentIdSecond); + assertTotalDuplication(); + } + + private List preparePaymentWrappers() { + List paymentWrappers = RandomBeans.randomListOf(2,PaymentWrapper.class); + paymentWrappers.forEach(pw -> { + pw.setCashFlowWrapper(new CashFlowWrapper( + RandomBeans.random(CashFlowLink.class), + RandomBeans.randomListOf(3, CashFlow.class) + )); + pw.getCashFlowWrapper().getCashFlows().forEach(cf -> cf.setObjType(PaymentChangeType.payment)); + PaymentWrapperTestUtil.setCurrent(pw, true); + }); + PaymentWrapperTestUtil.setInvoiceIdAndPaymentId(paymentWrappers.get(0), invoiceIdFirst, paymentIdFirst); + PaymentWrapperTestUtil.setInvoiceIdAndPaymentId(paymentWrappers.get(1), invoiceIdSecond, paymentIdSecond); + return paymentWrappers; + } + + private void assertPaymentWrapperFromDao(PaymentWrapper expected, String invoiceId, String paymentId) { + assertEquals(expected.getPayment(), paymentDao.get(invoiceId, paymentId)); + assertEquals(expected.getPaymentStatusInfo(), paymentStatusInfoDao.get(invoiceId, paymentId)); + assertEquals(expected.getPaymentPayerInfo(), paymentPayerInfoDao.get(invoiceId, paymentId)); + assertEquals(expected.getPaymentAdditionalInfo(), paymentAdditionalInfoDao.get(invoiceId, paymentId)); + assertEquals(expected.getPaymentRecurrentInfo(), paymentRecurrentInfoDao.get(invoiceId, paymentId)); + assertEquals(expected.getPaymentRiskData(), paymentRiskDataDao.get(invoiceId, paymentId)); + assertEquals(expected.getPaymentFee(), paymentFeeDao.get(invoiceId, paymentId)); + assertEquals(expected.getPaymentRoute(), paymentRouteDao.get(invoiceId, paymentId)); + assertEquals(expected.getCashFlowWrapper().getCashFlowLink(), cashFlowLinkDao.get(invoiceId, paymentId)); + assertEquals( + new HashSet<>(expected.getCashFlowWrapper().getCashFlows()), + new HashSet<>(cashFlowDao.getByObjId(expected.getCashFlowWrapper().getCashFlowLink().getId(), PaymentChangeType.payment)) + ); + } + + private void assertDuplication(String invoiceId, String paymentId) { + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_status_info", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_payer_info", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_additional_info", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_recurrent_info", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_risk_data", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_fee", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_route", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "cash_flow_link", invoiceId, paymentId, false)); + } + + private void assertTotalDuplication() { + assertEquals(2, countEntities(jdbcTemplate, "payment")); + assertEquals(2, countEntities(jdbcTemplate, "payment_status_info")); + assertEquals(2, countEntities(jdbcTemplate, "payment_payer_info")); + assertEquals(2, countEntities(jdbcTemplate, "payment_additional_info")); + assertEquals(2, countEntities(jdbcTemplate, "payment_recurrent_info")); + assertEquals(2, countEntities(jdbcTemplate, "payment_risk_data")); + assertEquals(2, countEntities(jdbcTemplate, "payment_fee")); + assertEquals(2, countEntities(jdbcTemplate, "payment_route")); + assertEquals(2, countEntities(jdbcTemplate, "cash_flow_link")); + assertEquals(6, countEntities(jdbcTemplate, "cash_flow")); + } +} diff --git a/src/test/java/dev/vality/newway/service/RateServiceTests.java b/src/test/java/dev/vality/newway/service/RateServiceTests.java index 6116cccf..80714f4f 100644 --- a/src/test/java/dev/vality/newway/service/RateServiceTests.java +++ b/src/test/java/dev/vality/newway/service/RateServiceTests.java @@ -23,13 +23,13 @@ public class RateServiceTests { @Test public void rateServiceTest() { - jdbcTemplate.execute("truncate table nw.rate cascade"); + jdbcTemplate.execute("truncate table dw.rate cascade"); String sourceId = "CBR"; rateService.handleEvents(RateSinkEventTestUtils.create(sourceId)); List rates = jdbcTemplate.query( - "SELECT * FROM nw.rate AS rate WHERE rate.source_id = ? AND rate.current", + "SELECT * FROM dw.rate AS rate WHERE rate.source_id = ? AND rate.current", new Object[]{sourceId}, new BeanPropertyRowMapper(Rate.class) ); @@ -38,7 +38,7 @@ public void rateServiceTest() { @Test public void rateServiceDuplicationTest() { - jdbcTemplate.execute("truncate table nw.rate cascade"); + jdbcTemplate.execute("truncate table dw.rate cascade"); String sourceId = "CBR"; List sinkEvents = RateSinkEventTestUtils.create(sourceId); @@ -46,7 +46,7 @@ public void rateServiceDuplicationTest() { rateService.handleEvents(sinkEvents); List rates = jdbcTemplate.query( - "SELECT * FROM nw.rate AS rate WHERE rate.source_id = ? AND rate.current", + "SELECT * FROM dw.rate AS rate WHERE rate.source_id = ? AND rate.current", new Object[]{sourceId}, new BeanPropertyRowMapper(Rate.class) ); @@ -55,7 +55,7 @@ public void rateServiceDuplicationTest() { @Test public void rateServiceDuplicationWhenPaymentSystemIsNullTest() { - jdbcTemplate.execute("truncate table nw.rate cascade"); + jdbcTemplate.execute("truncate table dw.rate cascade"); String sourceId = "CBR"; List sinkEvents = RateSinkEventTestUtils.create(sourceId, "payment_system"); @@ -63,7 +63,7 @@ public void rateServiceDuplicationWhenPaymentSystemIsNullTest() { rateService.handleEvents(sinkEvents); List rates = jdbcTemplate.query( - "SELECT * FROM nw.rate AS rate WHERE rate.source_id = ? AND rate.current", + "SELECT * FROM dw.rate AS rate WHERE rate.source_id = ? AND rate.current", new Object[]{sourceId}, new BeanPropertyRowMapper(Rate.class) ); diff --git a/src/test/java/dev/vality/newway/service/RecurrentPaymentToolServiceTest.java b/src/test/java/dev/vality/newway/service/RecurrentPaymentToolServiceTest.java index 31ce495b..210703be 100644 --- a/src/test/java/dev/vality/newway/service/RecurrentPaymentToolServiceTest.java +++ b/src/test/java/dev/vality/newway/service/RecurrentPaymentToolServiceTest.java @@ -33,7 +33,12 @@ public void handleEventsTest() { List events = buildEvent(recurrentId); recurrentPaymentToolService.handleEvents(events); - String sql = "select * from nw.recurrent_payment_tool where recurrent_payment_tool_id = :id"; + String sql = """ + select * + from dw.recurrent_payment_tool + where recurrent_payment_tool_id = :id + order by sequence_id, change_id asc + """; List recurrentPaymentTools = jdbcTemplate.query(sql, new MapSqlParameterSource("id", recurrentId), new BeanPropertyRowMapper<>( diff --git a/src/test/java/dev/vality/newway/util/CashFlowUtilTest.java b/src/test/java/dev/vality/newway/util/CashFlowUtilTest.java index 8173bff3..6c2b89d5 100644 --- a/src/test/java/dev/vality/newway/util/CashFlowUtilTest.java +++ b/src/test/java/dev/vality/newway/util/CashFlowUtilTest.java @@ -1,6 +1,7 @@ package dev.vality.newway.util; import dev.vality.damsel.domain.*; +import dev.vality.newway.model.CashFlowType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/src/test/java/dev/vality/newway/util/ContractorUtilTest.java b/src/test/java/dev/vality/newway/util/ContractorFactoryTest.java similarity index 83% rename from src/test/java/dev/vality/newway/util/ContractorUtilTest.java rename to src/test/java/dev/vality/newway/util/ContractorFactoryTest.java index 5e44d11b..68a490fd 100644 --- a/src/test/java/dev/vality/newway/util/ContractorUtilTest.java +++ b/src/test/java/dev/vality/newway/util/ContractorFactoryTest.java @@ -2,12 +2,13 @@ import dev.vality.damsel.domain.Contractor; import dev.vality.newway.TestData; +import dev.vality.newway.factory.contractor.ContractorFactory; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -class ContractorUtilTest { +class ContractorFactoryTest { public static final String CREATED_AT = "2021-05-18T16:46:22.405695Z"; public static final long SEQ_ID = 1L; @@ -24,8 +25,8 @@ void convertContractorWithoutCountryCode() { Integer changeId = 1; Integer claimEffectId = 2; - dev.vality.newway.domain.tables.pojos.Contractor actualContractor = ContractorUtil - .convertContractor(seqId, CREATED_AT, partyId, contractor, contractorId, changeId, claimEffectId); + dev.vality.newway.domain.tables.pojos.Contractor actualContractor = ContractorFactory + .build(seqId, CREATED_AT, partyId, contractor, contractorId, changeId, claimEffectId); assertNull(actualContractor.getInternationalLegalEntityCountryCode()); @@ -37,8 +38,8 @@ void convertContractorWithCountryCode() { String partyId = TestData.randomString(); String contractorId = TestData.randomString(); - dev.vality.newway.domain.tables.pojos.Contractor actualContractor = ContractorUtil - .convertContractor(SEQ_ID, CREATED_AT, partyId, contractor, contractorId, CHANGE_ID, CLAIM_EFFECT_ID); + dev.vality.newway.domain.tables.pojos.Contractor actualContractor = ContractorFactory + .build(SEQ_ID, CREATED_AT, partyId, contractor, contractorId, CHANGE_ID, CLAIM_EFFECT_ID); assertEquals(SEQ_ID, (long) actualContractor.getSequenceId()); assertEquals(contractorId, actualContractor.getContractorId()); diff --git a/src/test/java/dev/vality/newway/utils/JdbcUtil.java b/src/test/java/dev/vality/newway/utils/JdbcUtil.java new file mode 100644 index 00000000..bf54f6a5 --- /dev/null +++ b/src/test/java/dev/vality/newway/utils/JdbcUtil.java @@ -0,0 +1,42 @@ +package dev.vality.newway.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.util.Objects; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class JdbcUtil { + + public static int countEntities(JdbcTemplate jdbcTemplate, + String table) { + String query = "SELECT count(*) FROM dw." + table; + return Objects.requireNonNull( + jdbcTemplate.queryForObject(query, Integer.class)); + } + + public static int countPaymentEntity(JdbcTemplate jdbcTemplate, + String table, + String invoiceId, + String paymentId, + boolean withCurrent) { + String query = "SELECT count(*) FROM dw." + table + " WHERE invoice_id = ? AND payment_id = ?"; + if (withCurrent) { + query += " AND current"; + } + return Objects.requireNonNull( + jdbcTemplate.queryForObject(query, new Object[]{invoiceId, paymentId}, Integer.class)); + } + + public static int countInvoiceEntity(JdbcTemplate jdbcTemplate, + String table, + String invoiceId, + boolean withCurrent) { + String query = "SELECT count(*) FROM dw." + table + " WHERE invoice_id = ?"; + if (withCurrent) { + query += " AND current"; + } + return Objects.requireNonNull(jdbcTemplate.queryForObject(query, new Object[]{invoiceId}, Integer.class)); + } +} diff --git a/src/test/java/dev/vality/newway/utils/MockUtils.java b/src/test/java/dev/vality/newway/utils/MockUtils.java index 40b89c8e..322500a6 100644 --- a/src/test/java/dev/vality/newway/utils/MockUtils.java +++ b/src/test/java/dev/vality/newway/utils/MockUtils.java @@ -3,9 +3,15 @@ import dev.vality.damsel.base.Content; import dev.vality.damsel.domain.*; import dev.vality.geck.common.util.TypeUtil; +import dev.vality.newway.domain.enums.PaymentChangeType; +import dev.vality.newway.domain.tables.pojos.CashFlow; +import dev.vality.newway.domain.tables.pojos.CashFlowLink; +import dev.vality.newway.model.CashFlowWrapper; import java.nio.ByteBuffer; import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -16,7 +22,7 @@ public static Invoice buildInvoice(String invoiceId) { .setId(invoiceId) .setOwnerId("party_1") .setShopId("shop_id") - .setCreatedAt(TypeUtil.temporalToString(LocalDateTime.now())) + .setCreatedAt(TypeUtil.temporalToString(LocalDateTime.now().truncatedTo(ChronoUnit.MICROS))) .setStatus(InvoiceStatus.unpaid(new InvoiceUnpaid())) .setDetails(new InvoiceDetails() .setProduct("prod") @@ -26,7 +32,7 @@ public static Invoice buildInvoice(String invoiceId) { .setProduct("product") .setPrice(new Cash(12, new CurrencyRef("RUB"))) .setMetadata(new HashMap<>()))))) - .setDue(TypeUtil.temporalToString(LocalDateTime.now())) + .setDue(TypeUtil.temporalToString(LocalDateTime.now().truncatedTo(ChronoUnit.MICROS))) .setCost(new Cash().setAmount(1).setCurrency(new CurrencyRef("RUB"))) .setContext(new Content("type", ByteBuffer.wrap(new byte[]{}))); } @@ -34,7 +40,7 @@ public static Invoice buildInvoice(String invoiceId) { public static InvoicePayment buildPayment(String paymentId) { return new InvoicePayment() .setId(paymentId) - .setCreatedAt(TypeUtil.temporalToString(LocalDateTime.now())) + .setCreatedAt(TypeUtil.temporalToString(LocalDateTime.now().truncatedTo(ChronoUnit.MICROS))) .setStatus(InvoicePaymentStatus.pending(new InvoicePaymentPending())) .setCost(new Cash(11, new CurrencyRef("RUB"))) .setDomainRevision(1) @@ -47,4 +53,31 @@ public static InvoicePayment buildPayment(String paymentId) { .setRecurrentParent(new RecurrentParentPayment("1", "2")) .setContactInfo(new ContactInfo()))); } + + public static CashFlowWrapper buildCashFlowWrapper(String invoiceId, String paymentId, Long sequenceId, Integer changeId) { + var link = new CashFlowLink(); + link.setInvoiceId(invoiceId); + link.setPaymentId(paymentId); + link.setEventCreatedAt(LocalDateTime.now().truncatedTo(ChronoUnit.MICROS)); + link.setSequenceId(sequenceId); + link.setChangeId(changeId); + + List cashFlows = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + var cashFlow = new CashFlow(); + cashFlow.setAmount((long) i); + cashFlow.setObjType(PaymentChangeType.payment); + cashFlow.setCurrencyCode("RUB"); + cashFlow.setDetails("details " + i); + cashFlow.setSourceAccountId((long) i); + cashFlow.setSourceAccountType(dev.vality.newway.domain.enums.CashFlowAccount.merchant); + cashFlow.setSourceAccountTypeValue("source_account_type_value"); + cashFlow.setDestinationAccountId((long) i + sequenceId); + cashFlow.setDestinationAccountType(dev.vality.newway.domain.enums.CashFlowAccount.external); + cashFlow.setDestinationAccountTypeValue("destination_account_type_value"); + cashFlows.add(cashFlow); + } + + return new CashFlowWrapper(link, cashFlows); + } } diff --git a/src/test/java/dev/vality/newway/utils/PaymentWrapperTestUtil.java b/src/test/java/dev/vality/newway/utils/PaymentWrapperTestUtil.java new file mode 100644 index 00000000..0b0c1c46 --- /dev/null +++ b/src/test/java/dev/vality/newway/utils/PaymentWrapperTestUtil.java @@ -0,0 +1,77 @@ +package dev.vality.newway.utils; + +import dev.vality.newway.model.InvoicingKey; +import dev.vality.newway.model.PaymentWrapper; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class PaymentWrapperTestUtil { + + public static void setCurrent(PaymentWrapper wrapper, boolean current) { + if (wrapper.getPaymentStatusInfo() != null) { + wrapper.getPaymentStatusInfo().setCurrent(current); + } + if (wrapper.getPaymentAdditionalInfo() != null) { + wrapper.getPaymentAdditionalInfo().setCurrent(current); + } + if (wrapper.getPaymentRecurrentInfo() != null) { + wrapper.getPaymentRecurrentInfo().setCurrent(current); + } + if (wrapper.getPaymentRiskData() != null) { + wrapper.getPaymentRiskData().setCurrent(current); + } + if (wrapper.getPaymentFee() != null) { + wrapper.getPaymentFee().setCurrent(current); + } + if (wrapper.getPaymentRoute() != null) { + wrapper.getPaymentRoute().setCurrent(current); + } + if (wrapper.getCashFlowWrapper() != null) { + wrapper.getCashFlowWrapper().getCashFlowLink().setCurrent(current); + } + } + + public static void setInvoiceIdAndPaymentId(PaymentWrapper wrapper, String invoiceId, String paymentId) { + if (wrapper.getPayment() != null) { + wrapper.getPayment().setInvoiceId(invoiceId); + wrapper.getPayment().setPaymentId(paymentId); + } + if (wrapper.getPaymentStatusInfo() != null) { + wrapper.getPaymentStatusInfo().setInvoiceId(invoiceId); + wrapper.getPaymentStatusInfo().setPaymentId(paymentId); + } + if (wrapper.getPaymentPayerInfo() != null) { + wrapper.getPaymentPayerInfo().setInvoiceId(invoiceId); + wrapper.getPaymentPayerInfo().setPaymentId(paymentId); + } + if (wrapper.getPaymentAdditionalInfo() != null) { + wrapper.getPaymentAdditionalInfo().setInvoiceId(invoiceId); + wrapper.getPaymentAdditionalInfo().setPaymentId(paymentId); + } + if (wrapper.getPaymentRecurrentInfo() != null) { + wrapper.getPaymentRecurrentInfo().setInvoiceId(invoiceId); + wrapper.getPaymentRecurrentInfo().setPaymentId(paymentId); + } + if (wrapper.getPaymentRiskData() != null) { + wrapper.getPaymentRiskData().setInvoiceId(invoiceId); + wrapper.getPaymentRiskData().setPaymentId(paymentId); + } + if (wrapper.getPaymentFee() != null) { + wrapper.getPaymentFee().setInvoiceId(invoiceId); + wrapper.getPaymentFee().setPaymentId(paymentId); + } + if (wrapper.getPaymentRoute() != null) { + wrapper.getPaymentRoute().setInvoiceId(invoiceId); + wrapper.getPaymentRoute().setPaymentId(paymentId); + } + if (wrapper.getCashFlowWrapper() != null) { + wrapper.getCashFlowWrapper().getCashFlowLink().setInvoiceId(invoiceId); + wrapper.getCashFlowWrapper().getCashFlowLink().setPaymentId(paymentId); + } + if (wrapper.getKey() != null) { + wrapper.setKey(InvoicingKey.buildKey(invoiceId, paymentId)); + } + } + +} From a6fa291f8762dde484a50ab90da41ec04d1e82eb Mon Sep 17 00:00:00 2001 From: mr-impossibru <64555470+mr-impossibru@users.noreply.github.com> Date: Mon, 30 May 2022 12:07:16 +0300 Subject: [PATCH 02/36] TD-249: fix refund aggregate (#49) --- .../dao/invoicing/impl/RefundDaoImpl.java | 32 +++-- src/main/resources/db/migration/V1__init.sql | 132 +++++++++++++++++- .../java/dev/vality/newway/dao/DaoTests.java | 2 + 3 files changed, 155 insertions(+), 11 deletions(-) diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/RefundDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/RefundDaoImpl.java index 44d9889c..ce1b32cb 100644 --- a/src/main/java/dev/vality/newway/dao/invoicing/impl/RefundDaoImpl.java +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/RefundDaoImpl.java @@ -64,16 +64,28 @@ public void updateCommissions(Long rfndId) throws DaoException { MapSqlParameterSource params = new MapSqlParameterSource("rfndId", rfndId).addValue("objType", PaymentChangeType.refund.name()); this.getNamedParameterJdbcTemplate().update( - "UPDATE dw.refund SET fee = (SELECT dw.get_refund_fee(dw.cash_flow.*) " + - "FROM dw.cash_flow WHERE obj_id = :rfndId " + - "AND obj_type = CAST(:objType as dw.payment_change_type)), " + - "provider_fee = (SELECT dw.get_refund_provider_fee(dw.cash_flow.*) " + - "FROM dw.cash_flow WHERE obj_id = :rfndId " + - "AND obj_type = CAST(:objType as dw.payment_change_type)), " + - "external_fee = (SELECT dw.get_refund_external_fee(dw.cash_flow.*) " + - "FROM dw.cash_flow WHERE obj_id = :rfndId " + - "AND obj_type = CAST(:objType as dw.payment_change_type)) " + - "WHERE id = :rfndId", + """ + UPDATE dw.refund + SET + fee = ( + SELECT dw.get_refund_fee(dw.cash_flow.*) + FROM dw.cash_flow + WHERE obj_id = :rfndId + AND obj_type = CAST(:objType as dw.payment_change_type) + ), + provider_fee = ( + SELECT dw.get_refund_provider_fee(dw.cash_flow.*) + FROM dw.cash_flow + WHERE obj_id = :rfndId + AND obj_type = CAST(:objType as dw.payment_change_type) + ), + external_fee = ( + SELECT dw.get_refund_external_fee(dw.cash_flow.*) + FROM dw.cash_flow + WHERE obj_id = :rfndId + AND obj_type = CAST(:objType as dw.payment_change_type) + ) + WHERE id = :rfndId""", params); } diff --git a/src/main/resources/db/migration/V1__init.sql b/src/main/resources/db/migration/V1__init.sql index 0fbee282..08a3f75f 100644 --- a/src/main/resources/db/migration/V1__init.sql +++ b/src/main/resources/db/migration/V1__init.sql @@ -1677,4 +1677,134 @@ CREATE TABLE dw.withdrawal_session CREATE INDEX withdrawal_session_event_created_at_idx ON dw.withdrawal_session USING btree (event_created_at); CREATE INDEX withdrawal_session_event_occured_at_idx ON dw.withdrawal_session USING btree (event_occured_at); -CREATE INDEX withdrawal_session_id_idx ON dw.withdrawal_session USING btree (withdrawal_session_id); \ No newline at end of file +CREATE INDEX withdrawal_session_id_idx ON dw.withdrawal_session USING btree (withdrawal_session_id); + + +-- FUNCTION + +CREATE FUNCTION dw.get_cashflow_sum(_cash_flow dw.cash_flow, obj_type dw.payment_change_type, + source_account_type dw.cash_flow_account, + source_account_type_values character varying[], + destination_account_type dw.cash_flow_account, + destination_account_type_values character varying[]) RETURNS bigint + LANGUAGE plpgsql + IMMUTABLE PARALLEL SAFE +AS +$_$ +begin + return ( + coalesce( + ( + select amount + from (select ($1).*) as cash_flow + where cash_flow.obj_type = $2 + and cash_flow.source_account_type = $3 + and cash_flow.source_account_type_value = ANY ($4) + and cash_flow.destination_account_type = $5 + and cash_flow.destination_account_type_value = ANY ($6) + and ( + (cash_flow.obj_type = 'adjustment' and cash_flow.adj_flow_type = 'new_cash_flow') + or (cash_flow.obj_type != 'adjustment' and cash_flow.adj_flow_type is null) + ) + ), 0) + ); +end; +$_$; + + +CREATE FUNCTION dw.cashflow_sum_finalfunc(amount bigint) RETURNS bigint + LANGUAGE plpgsql + IMMUTABLE PARALLEL SAFE +AS +$$ +begin + return amount; +end; +$$; + + +CREATE FUNCTION dw.get_refund_fee_sfunc(amount bigint, cash_flow dw.cash_flow) RETURNS bigint + LANGUAGE plpgsql + IMMUTABLE PARALLEL SAFE +AS +$_$ +begin + return $1 + ( + dw.get_cashflow_sum( + $2, + 'refund'::dw.payment_change_type, + 'merchant'::dw.cash_flow_account, + '{"settlement"}', + 'system'::dw.cash_flow_account, + '{"settlement"}' + ) + ); +end; +$_$; + + +CREATE FUNCTION dw.get_refund_external_fee_sfunc(amount bigint, cash_flow dw.cash_flow) RETURNS bigint + LANGUAGE plpgsql + IMMUTABLE PARALLEL SAFE +AS +$_$ +begin + return $1 + ( + dw.get_cashflow_sum( + $2, + 'refund'::dw.payment_change_type, + 'system'::dw.cash_flow_account, + '{"settlement"}', + 'external'::dw.cash_flow_account, + '{"income", "outcome"}' + ) + ); +end; +$_$; + + +CREATE FUNCTION dw.get_refund_provider_fee_sfunc(amount bigint, cash_flow dw.cash_flow) RETURNS bigint + LANGUAGE plpgsql + IMMUTABLE PARALLEL SAFE +AS +$_$ +begin + return $1 + ( + dw.get_cashflow_sum( + $2, + 'refund'::dw.payment_change_type, + 'system'::dw.cash_flow_account, + '{"settlement"}', + 'provider'::dw.cash_flow_account, + '{"settlement"}' + ) + ); +end; +$_$; + + +-- AGGREGATE + +CREATE AGGREGATE dw.get_refund_external_fee(dw.cash_flow) ( + SFUNC = dw.get_refund_external_fee_sfunc, + STYPE = bigint, + INITCOND = '0', + FINALFUNC = dw.cashflow_sum_finalfunc, + PARALLEL = safe + ); + +CREATE AGGREGATE dw.get_refund_fee(dw.cash_flow) ( + SFUNC = dw.get_refund_fee_sfunc, + STYPE = bigint, + INITCOND = '0', + FINALFUNC = dw.cashflow_sum_finalfunc, + PARALLEL = safe + ); + +CREATE AGGREGATE dw.get_refund_provider_fee(dw.cash_flow) ( + SFUNC = dw.get_refund_provider_fee_sfunc, + STYPE = bigint, + INITCOND = '0', + FINALFUNC = dw.cashflow_sum_finalfunc, + PARALLEL = safe + ); \ No newline at end of file diff --git a/src/test/java/dev/vality/newway/dao/DaoTests.java b/src/test/java/dev/vality/newway/dao/DaoTests.java index 9ddf8b4b..c5e4a068 100644 --- a/src/test/java/dev/vality/newway/dao/DaoTests.java +++ b/src/test/java/dev/vality/newway/dao/DaoTests.java @@ -488,6 +488,8 @@ public void refundDaoTest() { assertEquals(refund, refundGet); refundDao.updateNotCurrent(refund.getId()); + refundDao.updateCommissions(refund.getId()); + assertThrows(NotFoundException.class, () -> refundDao.get(refund.getInvoiceId(), refund.getPaymentId(), refund.getRefundId())); } From 0b9fe90a9b042030525602f6f32e7bf9d993dc9f Mon Sep 17 00:00:00 2001 From: Egor Cherniak Date: Wed, 20 Jul 2022 13:32:15 +0300 Subject: [PATCH 03/36] new kafka config + iam support (#52) --- pom.xml | 10 ++- .../dev/vality/newway/config/KafkaConfig.java | 53 ++++----------- .../properties/KafkaConsumerProperties.java | 5 -- .../config/properties/KafkaSslProperties.java | 23 ------- .../dominant/impl/ProviderHandler.java | 3 - .../impl/WithdrawalProviderHandler.java | 68 ------------------- .../newway/util/PaymentMethodUtils.java | 27 +------- src/main/resources/application.yml | 26 +++---- .../dominant/impl/ProviderHandlerTest.java | 2 +- .../impl/WithdrawalProviderHandlerTest.java | 41 ----------- .../RecurrentPaymentToolServiceTest.java | 6 +- .../dev/vality/newway/utils/MockUtils.java | 2 +- 12 files changed, 34 insertions(+), 232 deletions(-) delete mode 100644 src/main/java/dev/vality/newway/config/properties/KafkaSslProperties.java delete mode 100644 src/main/java/dev/vality/newway/handler/dominant/impl/WithdrawalProviderHandler.java delete mode 100644 src/test/java/dev/vality/newway/handler/dominant/impl/WithdrawalProviderHandlerTest.java diff --git a/pom.xml b/pom.xml index 0168d8d3..08c29a14 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ dev.vality service-parent-pom - 1.0.16 + 1.0.18 newway @@ -65,7 +65,7 @@ de.codecentric spring-boot-admin-starter-client - 2.6.6 + 2.7.1 org.postgresql @@ -123,6 +123,10 @@ io.micrometer micrometer-registry-prometheus + + software.amazon.msk + aws-msk-iam-auth + @@ -188,7 +192,7 @@ dev.vality testcontainers-annotations - 1.4.0 + 1.4.1 test diff --git a/src/main/java/dev/vality/newway/config/KafkaConfig.java b/src/main/java/dev/vality/newway/config/KafkaConfig.java index 85c8bdca..ada9bbe9 100644 --- a/src/main/java/dev/vality/newway/config/KafkaConfig.java +++ b/src/main/java/dev/vality/newway/config/KafkaConfig.java @@ -3,7 +3,6 @@ import dev.vality.kafka.common.util.ExponentialBackOffDefaultErrorHandlerFactory; import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.config.properties.KafkaConsumerProperties; -import dev.vality.newway.config.properties.KafkaSslProperties; import dev.vality.newway.serde.PayoutEventDeserializer; import dev.vality.newway.serde.SinkEventDeserializer; import dev.vality.payout.manager.Event; @@ -14,6 +13,7 @@ import org.apache.kafka.common.security.auth.SecurityProtocol; import org.apache.kafka.common.serialization.StringDeserializer; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.kafka.KafkaProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -30,58 +30,31 @@ @Configuration @RequiredArgsConstructor -@EnableConfigurationProperties(KafkaSslProperties.class) @SuppressWarnings("LineLength") public class KafkaConfig { + private final KafkaProperties kafkaProperties; private final KafkaConsumerProperties kafkaConsumerProperties; @Value("${kafka.topics.party-management.consumer.group-id}") private String partyConsumerGroup; - @Value("${kafka.client-id}") - private String clientId; - @Value("${kafka.bootstrap-servers}") - private String bootstrapServers; @Bean - public Map consumerConfigs(KafkaSslProperties kafkaSslProperties) { - return createConsumerConfig(kafkaSslProperties); + public Map consumerConfigs() { + return createConsumerConfig(); } - private Map createConsumerConfig(KafkaSslProperties kafkaSslProperties) { - Map props = new HashMap<>(); - props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); + private Map createConsumerConfig() { + Map props = kafkaProperties.buildConsumerProperties(); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, SinkEventDeserializer.class); props.put(ConsumerConfig.GROUP_ID_CONFIG, kafkaConsumerProperties.getGroupId()); - props.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId); - props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, kafkaConsumerProperties.isEnableAutoCommit()); - props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, kafkaConsumerProperties.getAutoOffsetReset()); - props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, kafkaConsumerProperties.getMaxPollRecords()); - props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, kafkaConsumerProperties.getSessionTimeoutMs()); - props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, kafkaConsumerProperties.getMaxPollIntervalMs()); - configureSsl(props, kafkaSslProperties); return props; } - private void configureSsl(Map props, KafkaSslProperties kafkaSslProperties) { - if (kafkaSslProperties.isEnabled()) { - props.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, SecurityProtocol.SSL.name()); - props.put(SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG, - new File(kafkaSslProperties.getTrustStoreLocation()).getAbsolutePath()); - props.put(SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG, kafkaSslProperties.getTrustStorePassword()); - props.put(SslConfigs.SSL_KEYSTORE_TYPE_CONFIG, kafkaSslProperties.getKeyStoreType()); - props.put(SslConfigs.SSL_TRUSTSTORE_TYPE_CONFIG, kafkaSslProperties.getTrustStoreType()); - props.put(SslConfigs.SSL_KEYSTORE_LOCATION_CONFIG, - new File(kafkaSslProperties.getKeyStoreLocation()).getAbsolutePath()); - props.put(SslConfigs.SSL_KEYSTORE_PASSWORD_CONFIG, kafkaSslProperties.getKeyStorePassword()); - props.put(SslConfigs.SSL_KEY_PASSWORD_CONFIG, kafkaSslProperties.getKeyPassword()); - } - } - @Bean - public ConsumerFactory consumerFactory(KafkaSslProperties kafkaSslProperties) { - return new DefaultKafkaConsumerFactory<>(consumerConfigs(kafkaSslProperties)); + public ConsumerFactory consumerFactory() { + return new DefaultKafkaConsumerFactory<>(consumerConfigs()); } @Bean @@ -127,10 +100,9 @@ public KafkaListenerContainerFactory> payoutContainerFactory( - KafkaSslProperties kafkaSslProperties) { + public KafkaListenerContainerFactory> payoutContainerFactory() { DefaultKafkaConsumerFactory kafkaConsumerFactory = - new DefaultKafkaConsumerFactory<>(createConsumerConfig(kafkaSslProperties)); + new DefaultKafkaConsumerFactory<>(createConsumerConfig()); kafkaConsumerFactory.setValueDeserializer(new PayoutEventDeserializer()); ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); @@ -157,9 +129,8 @@ public KafkaListenerContainerFactory> partyManagementContainerFactory( - KafkaSslProperties kafkaSslProperties) { - Map configs = createConsumerConfig(kafkaSslProperties); + public KafkaListenerContainerFactory> partyManagementContainerFactory() { + Map configs = createConsumerConfig(); configs.put(ConsumerConfig.GROUP_ID_CONFIG, partyConsumerGroup); ConsumerFactory consumerFactory = new DefaultKafkaConsumerFactory<>(configs); return createConcurrentFactory(consumerFactory, kafkaConsumerProperties.getPartyManagementConcurrency()); diff --git a/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java b/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java index f82498b1..596a83c8 100644 --- a/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java +++ b/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java @@ -11,12 +11,7 @@ @ConfigurationProperties(prefix = "kafka.consumer") public class KafkaConsumerProperties { - private String autoOffsetReset; - private boolean enableAutoCommit; private String groupId; - private int maxPollRecords; - private int maxPollIntervalMs; - private int sessionTimeoutMs; private int invoicingConcurrency; private int recurrentPaymentToolConcurrency; private int partyManagementConcurrency; diff --git a/src/main/java/dev/vality/newway/config/properties/KafkaSslProperties.java b/src/main/java/dev/vality/newway/config/properties/KafkaSslProperties.java deleted file mode 100644 index 5438ac36..00000000 --- a/src/main/java/dev/vality/newway/config/properties/KafkaSslProperties.java +++ /dev/null @@ -1,23 +0,0 @@ -package dev.vality.newway.config.properties; - -import lombok.Getter; -import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -@Getter -@Setter -@Component -@ConfigurationProperties(prefix = "kafka.ssl") -public class KafkaSslProperties { - - private String trustStorePassword; - private String trustStoreLocation; - private String keyStorePassword; - private String keyPassword; - private String keyStoreLocation; - private boolean enabled; - private String keyStoreType; - private String trustStoreType; - -} diff --git a/src/main/java/dev/vality/newway/handler/dominant/impl/ProviderHandler.java b/src/main/java/dev/vality/newway/handler/dominant/impl/ProviderHandler.java index aaeaa1f8..805045ae 100644 --- a/src/main/java/dev/vality/newway/handler/dominant/impl/ProviderHandler.java +++ b/src/main/java/dev/vality/newway/handler/dominant/impl/ProviderHandler.java @@ -50,9 +50,6 @@ public Provider convertToDatabaseObject(ProviderObject providerObject, Long vers provider.setDescription(data.getDescription()); provider.setProxyRefId(data.getProxy().getRef().getId()); provider.setProxyAdditionalJson(JsonUtil.objectToJsonString(data.getProxy().getAdditional())); - if (data.isSetTerminal()) { - provider.setTerminalJson(JsonUtil.thriftBaseToJsonString(data.getTerminal())); - } if (data.isSetAbsAccount()) { provider.setAbsAccount(data.getAbsAccount()); } diff --git a/src/main/java/dev/vality/newway/handler/dominant/impl/WithdrawalProviderHandler.java b/src/main/java/dev/vality/newway/handler/dominant/impl/WithdrawalProviderHandler.java deleted file mode 100644 index a080b6a9..00000000 --- a/src/main/java/dev/vality/newway/handler/dominant/impl/WithdrawalProviderHandler.java +++ /dev/null @@ -1,68 +0,0 @@ -package dev.vality.newway.handler.dominant.impl; - -import dev.vality.damsel.domain.WithdrawalProviderObject; -import dev.vality.newway.dao.dominant.iface.DomainObjectDao; -import dev.vality.newway.dao.dominant.impl.WithdrawalProviderDaoImpl; -import dev.vality.newway.domain.tables.pojos.WithdrawalProvider; -import dev.vality.newway.handler.dominant.AbstractDominantHandler; -import dev.vality.newway.util.JsonUtil; -import org.springframework.stereotype.Component; - -import java.util.Map; -import java.util.stream.Collectors; - -@Component -public class WithdrawalProviderHandler - extends AbstractDominantHandler { - - private final WithdrawalProviderDaoImpl withdrawalProviderDao; - - public WithdrawalProviderHandler(WithdrawalProviderDaoImpl withdrawalProviderDao) { - this.withdrawalProviderDao = withdrawalProviderDao; - } - - @Override - protected DomainObjectDao getDomainObjectDao() { - return withdrawalProviderDao; - } - - @Override - protected WithdrawalProviderObject getTargetObject() { - return getDomainObject().getWithdrawalProvider(); - } - - @Override - protected Integer getTargetObjectRefId() { - return getTargetObject().getRef().getId(); - } - - @Override - protected boolean acceptDomainObject() { - return getDomainObject().isSetWithdrawalProvider(); - } - - @Override - public WithdrawalProvider convertToDatabaseObject(WithdrawalProviderObject withdrawalProviderObject, Long versionId, - boolean current) { - WithdrawalProvider withdrawalProvider = new WithdrawalProvider(); - withdrawalProvider.setVersionId(versionId); - withdrawalProvider.setWithdrawalProviderRefId(getTargetObjectRefId()); - var data = withdrawalProviderObject.getData(); - withdrawalProvider.setName(data.getName()); - withdrawalProvider.setDescription(data.getDescription()); - withdrawalProvider.setProxyRefId(data.getProxy().getRef().getId()); - withdrawalProvider.setProxyAdditionalJson(JsonUtil.objectToJsonString(data.getProxy().getAdditional())); - withdrawalProvider.setIdentity(data.getIdentity()); - if (data.isSetWithdrawalTerms()) { - withdrawalProvider.setWithdrawalTermsJson(JsonUtil.thriftBaseToJsonString(data.getWithdrawalTerms())); - } - if (data.isSetAccounts()) { - Map accountsMap = data.getAccounts().entrySet() - .stream() - .collect(Collectors.toMap(e -> e.getKey().getSymbolicCode(), e -> e.getValue().getSettlement())); - withdrawalProvider.setAccountsJson(JsonUtil.objectToJsonString(accountsMap)); - } - withdrawalProvider.setCurrent(current); - return withdrawalProvider; - } -} diff --git a/src/main/java/dev/vality/newway/util/PaymentMethodUtils.java b/src/main/java/dev/vality/newway/util/PaymentMethodUtils.java index 4b703c25..e80edb05 100644 --- a/src/main/java/dev/vality/newway/util/PaymentMethodUtils.java +++ b/src/main/java/dev/vality/newway/util/PaymentMethodUtils.java @@ -15,32 +15,7 @@ public static Optional getPaymentMethodRefIdByBankCard( return paymentMethod.get() .filter(PaymentMethod::isSetBankCard) .map(PaymentMethod::getBankCard) - .flatMap(bankCard -> Optional.ofNullable(bankCard.getPaymentSystem()).map(PaymentSystemRef::getId)) - .or(() -> paymentMethod.get() - .filter(PaymentMethod::isSetBankCardDeprecated) - .map(PaymentMethod::getBankCardDeprecated) - .map(Enum::name)) - .or(() -> paymentMethod.get() - .filter(PaymentMethod::isSetEmptyCvvBankCardDeprecated) - .map(PaymentMethod::getEmptyCvvBankCardDeprecated) - .map(legacyBankCardPaymentSystem -> EMPTY_CVV + legacyBankCardPaymentSystem.name())) - .or(() -> paymentMethod.get() - .filter(PaymentMethod::isSetTokenizedBankCardDeprecated) - .map(PaymentMethod::getTokenizedBankCardDeprecated) - .flatMap(PaymentMethodUtils::getTokenizedBankCardId)); - } - - private static Optional getTokenizedBankCardId(TokenizedBankCard tokenizedBankCard) { - Optional paymentSystemName = Optional.ofNullable(tokenizedBankCard.getPaymentSystem()) - .map(PaymentSystemRef::getId); - Optional paymentToken = Optional.ofNullable(tokenizedBankCard.getPaymentToken()) - .map(BankCardTokenServiceRef::getId); - - return paymentSystemName - .flatMap(name -> paymentToken - .map(tokenProviderName -> name + - TOKENIZED_BANK_CARD_SEPARATOR + - tokenProviderName)); + .flatMap(bankCard -> Optional.ofNullable(bankCard.getPaymentSystem()).map(PaymentSystemRef::getId)); } public static Optional getPaymentMethodRefIdByPaymentTerminal( diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8bae7297..b6e1d120 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -17,6 +17,16 @@ spring: reWriteBatchedInserts: true flyway: schemas: dw + kafka: + bootstrap-servers: localhost:9092 + client-id: daway + consumer: + enable-auto-commit: false + auto-offset-reset: earliest + max-poll-records: 20 + properties: + max.poll.interval.ms: 30000 + session.timeout.ms: 30000 management: server: @@ -42,24 +52,8 @@ management: include: health,info,prometheus kafka: - bootstrap-servers: localhost:9092 - client-id: daway - ssl: - enabled: false - trust-store-location: "test" - trust-store-password: "test" - key-store-location: "test" - key-store-password: "test" - key-password: "test" - key-store-type: PKCS12 - trust-store-type: PKCS12 consumer: group-id: "DawayListener" - enable-auto-commit: false - auto-offset-reset: earliest - max-poll-records: 20 - max-poll-interval-ms: 30000 - session-timeout-ms: 30000 invoicing-concurrency: 7 party-management-concurrency: 7 recurrent-payment-tool-concurrency: 7 diff --git a/src/test/java/dev/vality/newway/handler/dominant/impl/ProviderHandlerTest.java b/src/test/java/dev/vality/newway/handler/dominant/impl/ProviderHandlerTest.java index 8aacea3e..73c6fa0a 100644 --- a/src/test/java/dev/vality/newway/handler/dominant/impl/ProviderHandlerTest.java +++ b/src/test/java/dev/vality/newway/handler/dominant/impl/ProviderHandlerTest.java @@ -77,7 +77,7 @@ private RecurrentPaytoolsProvisionTerms buildRecurrentPaytools() { PaymentMethodSelector paymentMethodSelector = new PaymentMethodSelector(); paymentMethodSelector.setValue(Set.of(new PaymentMethodRef( PaymentMethod.bank_card(new BankCardPaymentMethod() - .setPaymentSystemDeprecated(LegacyBankCardPaymentSystem.visa))))); + .setPaymentSystem(new PaymentSystemRef("visa")))))); recurrentPaytoolsProvisionTerms.setPaymentMethods(paymentMethodSelector); CashValueSelector cashValueSelector = new CashValueSelector(); cashValueSelector.setValue(buildCash()); diff --git a/src/test/java/dev/vality/newway/handler/dominant/impl/WithdrawalProviderHandlerTest.java b/src/test/java/dev/vality/newway/handler/dominant/impl/WithdrawalProviderHandlerTest.java deleted file mode 100644 index cf26acb4..00000000 --- a/src/test/java/dev/vality/newway/handler/dominant/impl/WithdrawalProviderHandlerTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package dev.vality.newway.handler.dominant.impl; - -import dev.vality.damsel.domain.*; -import dev.vality.newway.dao.dominant.impl.WithdrawalProviderDaoImpl; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; - -import java.util.Map; - -public class WithdrawalProviderHandlerTest { - - @Mock - private WithdrawalProviderDaoImpl withdrawalProviderDao; - - @Test - public void convertToDatabaseObject() { - WithdrawalProviderObject withdrawalProviderObject = buildWithdrawalProviderObject(); - - WithdrawalProviderHandler handler = new WithdrawalProviderHandler(withdrawalProviderDao); - handler.setDomainObject(DomainObject.withdrawal_provider(withdrawalProviderObject)); - var withdrawalProvider = handler.convertToDatabaseObject(withdrawalProviderObject, 123L, true); - Assertions.assertNotNull(withdrawalProvider); - Assertions.assertEquals(withdrawalProvider.getName(), withdrawalProviderObject.getData().getName()); - Assertions.assertEquals(withdrawalProvider.getIdentity(), withdrawalProviderObject.getData().getIdentity()); - Assertions.assertTrue(withdrawalProvider.getAccountsJson().contains("RUB")); - } - - private WithdrawalProviderObject buildWithdrawalProviderObject() { - return new WithdrawalProviderObject() - .setRef(new WithdrawalProviderRef(1)) - .setData(new WithdrawalProvider() - .setName(dev.vality.testcontainers.annotations.util.RandomBeans.random(String.class)) - .setDescription(dev.vality.testcontainers.annotations.util.RandomBeans.random(String.class)) - .setProxy(new Proxy() - .setRef(new ProxyRef(dev.vality.testcontainers.annotations.util.RandomBeans.random(Integer.class))) - .setAdditional(Map.of(dev.vality.testcontainers.annotations.util.RandomBeans.random(String.class), dev.vality.testcontainers.annotations.util.RandomBeans.random(String.class)))) - .setIdentity(dev.vality.testcontainers.annotations.util.RandomBeans.random(String.class)) - .setAccounts(Map.of(new CurrencyRef("RUB"), new ProviderAccount(123)))); - } -} diff --git a/src/test/java/dev/vality/newway/service/RecurrentPaymentToolServiceTest.java b/src/test/java/dev/vality/newway/service/RecurrentPaymentToolServiceTest.java index 210703be..61630345 100644 --- a/src/test/java/dev/vality/newway/service/RecurrentPaymentToolServiceTest.java +++ b/src/test/java/dev/vality/newway/service/RecurrentPaymentToolServiceTest.java @@ -108,12 +108,10 @@ private List buildEvent(String recurrentId) { .setPaymentResource(new DisposablePaymentResource() .setPaymentTool(PaymentTool.bank_card(new BankCard() .setToken("kkekekek_token") - .setPaymentSystemDeprecated( - LegacyBankCardPaymentSystem.amex) + .setPaymentSystem(new PaymentSystemRef("amex")) .setBin("bin") .setLastDigits("masked") - .setTokenProviderDeprecated( - LegacyBankCardTokenProvider.applepay) + .setPaymentToken(new BankCardTokenServiceRef("applepay")) .setIssuerCountry(CountryCode.ABH) .setBankName("bank_name") .setMetadata(Map.of("kek", diff --git a/src/test/java/dev/vality/newway/utils/MockUtils.java b/src/test/java/dev/vality/newway/utils/MockUtils.java index 322500a6..eb0db646 100644 --- a/src/test/java/dev/vality/newway/utils/MockUtils.java +++ b/src/test/java/dev/vality/newway/utils/MockUtils.java @@ -49,7 +49,7 @@ public static InvoicePayment buildPayment(String paymentId) { new RecurrentPayer() .setPaymentTool(PaymentTool.payment_terminal( new PaymentTerminal() - .setTerminalTypeDeprecated(LegacyTerminalPaymentProvider.alipay))) + .setPaymentService(new PaymentServiceRef("alipay")))) .setRecurrentParent(new RecurrentParentPayment("1", "2")) .setContactInfo(new ContactInfo()))); } From 077cb043dfe2fc6c91ffd348b341adb4727e8e44 Mon Sep 17 00:00:00 2001 From: Alexander Romanov Date: Wed, 27 Jul 2022 17:02:16 +0300 Subject: [PATCH 04/36] Bump damsel to 1.569-09e7a75 --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index 08c29a14..7a12a301 100644 --- a/pom.xml +++ b/pom.xml @@ -159,6 +159,8 @@ dev.vality damsel + + 1.569-09e7a75 dev.vality From ab8a179848f75c1c038d4c17305d60e2284658d6 Mon Sep 17 00:00:00 2001 From: Inal Arsanukaev Date: Wed, 3 Aug 2022 12:53:34 +0300 Subject: [PATCH 05/36] Added counting max for country (#54) Co-authored-by: Inal Arsanukaev --- .../dao/dominant/impl/DominantDaoImpl.java | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/main/java/dev/vality/newway/dao/dominant/impl/DominantDaoImpl.java b/src/main/java/dev/vality/newway/dao/dominant/impl/DominantDaoImpl.java index 5a2cb82d..773dd2b5 100644 --- a/src/main/java/dev/vality/newway/dao/dominant/impl/DominantDaoImpl.java +++ b/src/main/java/dev/vality/newway/dao/dominant/impl/DominantDaoImpl.java @@ -10,6 +10,8 @@ import javax.sql.DataSource; +import static org.jooq.impl.DSL.max; + @Component public class DominantDaoImpl extends AbstractGenericDao implements DominantDao { @@ -19,25 +21,28 @@ public DominantDaoImpl(DataSource dataSource) { @Override public Long getLastVersionId() throws DaoException { - Query query = getDslContext().select(DSL.max(DSL.field("version_id"))).from( - getDslContext().select(Tables.CALENDAR.VERSION_ID.max().as("version_id")).from(Tables.CALENDAR) - .unionAll(getDslContext().select(Tables.CATEGORY.VERSION_ID.max().as("version_id")).from(Tables.CATEGORY)) - .unionAll(getDslContext().select(Tables.CURRENCY.VERSION_ID.max().as("version_id")).from(Tables.CURRENCY)) - .unionAll(getDslContext().select(Tables.INSPECTOR.VERSION_ID.max().as("version_id")).from(Tables.INSPECTOR)) - .unionAll(getDslContext().select(Tables.PAYMENT_INSTITUTION.VERSION_ID.max().as("version_id")) + Query query = getDslContext().select(max(DSL.field("max"))).from( + getDslContext().select(max(Tables.CALENDAR.VERSION_ID)).from(Tables.CALENDAR) + .unionAll(getDslContext().select(max(Tables.CATEGORY.VERSION_ID)).from(Tables.CATEGORY)) + .unionAll(getDslContext().select(max(Tables.COUNTRY.VERSION_ID)).from(Tables.COUNTRY)) + .unionAll(getDslContext().select(max(Tables.CURRENCY.VERSION_ID)).from(Tables.CURRENCY)) + .unionAll(getDslContext().select(max(Tables.INSPECTOR.VERSION_ID)).from(Tables.INSPECTOR)) + .unionAll(getDslContext().select(max(Tables.PAYMENT_INSTITUTION.VERSION_ID)) .from(Tables.PAYMENT_INSTITUTION)) - .unionAll(getDslContext().select(Tables.PAYMENT_METHOD.VERSION_ID.max().as("version_id")) + .unionAll(getDslContext().select(max(Tables.PAYMENT_METHOD.VERSION_ID)) .from(Tables.PAYMENT_METHOD)) - .unionAll(getDslContext().select(Tables.PAYOUT_METHOD.VERSION_ID.max().as("version_id")) + .unionAll(getDslContext().select(max(Tables.PAYOUT_METHOD.VERSION_ID)) .from(Tables.PAYOUT_METHOD)) - .unionAll(getDslContext().select(Tables.PROVIDER.VERSION_ID.max().as("version_id")).from(Tables.PROVIDER)) - .unionAll(getDslContext().select(Tables.PROXY.VERSION_ID.max().as("version_id")).from(Tables.PROXY)) - .unionAll(getDslContext().select(Tables.TERMINAL.VERSION_ID.max().as("version_id")).from(Tables.TERMINAL)) - .unionAll(getDslContext().select(Tables.TERM_SET_HIERARCHY.VERSION_ID.max().as("version_id")) + .unionAll(getDslContext().select(max(Tables.PROVIDER.VERSION_ID)).from(Tables.PROVIDER)) + .unionAll(getDslContext().select(max(Tables.PROXY.VERSION_ID)).from(Tables.PROXY)) + .unionAll(getDslContext().select(max(Tables.TERMINAL.VERSION_ID)).from(Tables.TERMINAL)) + .unionAll(getDslContext().select(max(Tables.TERM_SET_HIERARCHY.VERSION_ID)) .from(Tables.TERM_SET_HIERARCHY)) - .unionAll(getDslContext().select(Tables.WITHDRAWAL_PROVIDER.VERSION_ID.max().as("version_id")) + .unionAll(getDslContext().select(max(Tables.TRADE_BLOC.VERSION_ID)) + .from(Tables.TRADE_BLOC)) + .unionAll(getDslContext().select(max(Tables.WITHDRAWAL_PROVIDER.VERSION_ID)) .from(Tables.WITHDRAWAL_PROVIDER)) - .unionAll(getDslContext().select(Tables.PAYMENT_ROUTING_RULE.VERSION_ID.max().as("version_id")) + .unionAll(getDslContext().select(max(Tables.PAYMENT_ROUTING_RULE.VERSION_ID)) .from(Tables.PAYMENT_ROUTING_RULE)) ); return fetchOne(query, Long.class); From 58014df3e0a7516ecf2d32681f2dfdb82c251811 Mon Sep 17 00:00:00 2001 From: Inal Arsanukaev Date: Tue, 23 Aug 2022 17:58:09 +0300 Subject: [PATCH 06/36] Changed withdrawal_session table column type (#56) Co-authored-by: Inal Arsanukaev --- .../withdrawal/session/WithdrawalSessionCreatedHandler.java | 4 +--- .../db/migration/V95__change_withdrawal_session_dest_type.sql | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 src/main/resources/db/migration/V95__change_withdrawal_session_dest_type.sql diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedHandler.java index b340a287..d582afac 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/session/WithdrawalSessionCreatedHandler.java @@ -12,7 +12,6 @@ import dev.vality.geck.filter.rule.PathConditionRule; import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.dao.withdrawal.session.iface.WithdrawalSessionDao; -import dev.vality.newway.domain.enums.BankCardPaymentSystem; import dev.vality.newway.domain.enums.DestinationResourceType; import dev.vality.newway.domain.enums.WithdrawalSessionStatus; import dev.vality.newway.domain.tables.pojos.WithdrawalSession; @@ -65,8 +64,7 @@ public void handle(TimestampedChange timestampedChange, MachineEvent event) { withdrawalSession.setDestinationCardBin(bankCard.getBin()); withdrawalSession.setDestinationCardMaskedPan(bankCard.getMaskedPan()); if (bankCard.isSetPaymentSystem()) { - withdrawalSession.setDestinationCardPaymentSystem( - BankCardPaymentSystem.valueOf(bankCard.getPaymentSystem().getId().toLowerCase(Locale.ROOT))); + withdrawalSession.setDestinationCardPaymentSystem(bankCard.getPaymentSystem().getId()); } withdrawalSession.setResourceBankCardBankName(bankCard.getBankName()); if (bankCard.isSetIssuerCountry()) { diff --git a/src/main/resources/db/migration/V95__change_withdrawal_session_dest_type.sql b/src/main/resources/db/migration/V95__change_withdrawal_session_dest_type.sql new file mode 100644 index 00000000..d6318933 --- /dev/null +++ b/src/main/resources/db/migration/V95__change_withdrawal_session_dest_type.sql @@ -0,0 +1,2 @@ +ALTER TABLE nw.withdrawal_session ALTER COLUMN destination_card_payment_system TYPE VARCHAR; +DROP TYPE nw.BANK_CARD_PAYMENT_SYSTEM; From 0068cd6f3eddfcf46815bdd173da93dfefbe5898 Mon Sep 17 00:00:00 2001 From: Inal Arsanukaev Date: Tue, 23 Aug 2022 18:28:46 +0300 Subject: [PATCH 07/36] Fix script --- .../db/migration/V2__change_withdrawal_session_dest_type.sql | 2 ++ .../db/migration/V95__change_withdrawal_session_dest_type.sql | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 src/main/resources/db/migration/V2__change_withdrawal_session_dest_type.sql delete mode 100644 src/main/resources/db/migration/V95__change_withdrawal_session_dest_type.sql diff --git a/src/main/resources/db/migration/V2__change_withdrawal_session_dest_type.sql b/src/main/resources/db/migration/V2__change_withdrawal_session_dest_type.sql new file mode 100644 index 00000000..aa11af95 --- /dev/null +++ b/src/main/resources/db/migration/V2__change_withdrawal_session_dest_type.sql @@ -0,0 +1,2 @@ +ALTER TABLE dw.withdrawal_session ALTER COLUMN destination_card_payment_system TYPE VARCHAR; +DROP TYPE dw.BANK_CARD_PAYMENT_SYSTEM; diff --git a/src/main/resources/db/migration/V95__change_withdrawal_session_dest_type.sql b/src/main/resources/db/migration/V95__change_withdrawal_session_dest_type.sql deleted file mode 100644 index d6318933..00000000 --- a/src/main/resources/db/migration/V95__change_withdrawal_session_dest_type.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE nw.withdrawal_session ALTER COLUMN destination_card_payment_system TYPE VARCHAR; -DROP TYPE nw.BANK_CARD_PAYMENT_SYSTEM; From 20ec64a4eee520afad7f75d8333ca8cb333b7fb2 Mon Sep 17 00:00:00 2001 From: Anatoly Karlov Date: Wed, 24 Aug 2022 20:48:06 +0700 Subject: [PATCH 08/36] OPS-165: add limit config (#60) * OPS-165: add limit config (#58) (cherry picked from commit def6250da0b98ccc0fe2f8c809e9b38f27dd999d) * OPS-165: add limit config --- pom.xml | 5 + .../dev/vality/newway/config/KafkaConfig.java | 12 +- .../properties/KafkaConsumerProperties.java | 1 + .../newway/dao/limiter/LimitConfigDao.java | 15 +++ .../dao/limiter/LimitConfigDaoImpl.java | 49 ++++++++ ...imitConfigMachineEventCopyFactoryImpl.java | 27 +++++ .../newway/handler/event/stock/Handler.java | 1 - .../limiter/LimitConfigCreatedHandler.java | 113 ++++++++++++++++++ .../impl/limiter/LimitConfigHandler.java | 8 ++ .../listener/LimitConfigKafkaListener.java | 36 ++++++ .../LimitConfigChangeMachineEventParser.java | 14 +++ .../LimitConfigChangeDeserializer.java | 14 +++ .../newway/service/LimitConfigService.java | 36 ++++++ src/main/resources/application.yml | 4 + .../db/migration/V3__add_limit_config.sql | 35 ++++++ .../KafkaPostgresqlSpringBootITest.java | 6 +- .../vality/newway/dao/LimitConfigDaoTest.java | 60 ++++++++++ .../vality/newway/kafka/KafkaProducer.java | 1 - .../kafka/LimitConfigKafkaListenerTest.java | 33 +++++ .../service/LimitConfigServiceTest.java | 75 ++++++++++++ .../newway/utils/LimitConfigGenerator.java | 67 +++++++++++ 21 files changed, 602 insertions(+), 10 deletions(-) create mode 100644 src/main/java/dev/vality/newway/dao/limiter/LimitConfigDao.java create mode 100644 src/main/java/dev/vality/newway/dao/limiter/LimitConfigDaoImpl.java create mode 100644 src/main/java/dev/vality/newway/factory/machine/event/LimitConfigMachineEventCopyFactoryImpl.java create mode 100644 src/main/java/dev/vality/newway/handler/event/stock/impl/limiter/LimitConfigCreatedHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/event/stock/impl/limiter/LimitConfigHandler.java create mode 100644 src/main/java/dev/vality/newway/listener/LimitConfigKafkaListener.java create mode 100644 src/main/java/dev/vality/newway/serde/LimitConfigChangeMachineEventParser.java create mode 100644 src/main/java/dev/vality/newway/serde/deserializer/LimitConfigChangeDeserializer.java create mode 100644 src/main/java/dev/vality/newway/service/LimitConfigService.java create mode 100644 src/main/resources/db/migration/V3__add_limit_config.sql create mode 100644 src/test/java/dev/vality/newway/dao/LimitConfigDaoTest.java create mode 100644 src/test/java/dev/vality/newway/kafka/LimitConfigKafkaListenerTest.java create mode 100644 src/test/java/dev/vality/newway/service/LimitConfigServiceTest.java create mode 100644 src/test/java/dev/vality/newway/utils/LimitConfigGenerator.java diff --git a/pom.xml b/pom.xml index 7a12a301..5ec32881 100644 --- a/pom.xml +++ b/pom.xml @@ -172,6 +172,11 @@ xrates-proto 1.23-bf0d62d + + dev.vality + limiter-proto + 1.32-6158184 + dev.vality shared-resources diff --git a/src/main/java/dev/vality/newway/config/KafkaConfig.java b/src/main/java/dev/vality/newway/config/KafkaConfig.java index ada9bbe9..9f602da0 100644 --- a/src/main/java/dev/vality/newway/config/KafkaConfig.java +++ b/src/main/java/dev/vality/newway/config/KafkaConfig.java @@ -7,14 +7,10 @@ import dev.vality.newway.serde.SinkEventDeserializer; import dev.vality.payout.manager.Event; import lombok.RequiredArgsConstructor; -import org.apache.kafka.clients.CommonClientConfigs; import org.apache.kafka.clients.consumer.ConsumerConfig; -import org.apache.kafka.common.config.SslConfigs; -import org.apache.kafka.common.security.auth.SecurityProtocol; import org.apache.kafka.common.serialization.StringDeserializer; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.kafka.KafkaProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; @@ -24,8 +20,6 @@ import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; import org.springframework.kafka.listener.ContainerProperties; -import java.io.File; -import java.util.HashMap; import java.util.Map; @Configuration @@ -136,6 +130,12 @@ public KafkaListenerContainerFactory> limitConfigContainerFactory( + ConsumerFactory consumerFactory) { + return createConcurrentFactory(consumerFactory, kafkaConsumerProperties.getLimitConfigConcurrency()); + } + private KafkaListenerContainerFactory> createConcurrentFactory( ConsumerFactory consumerFactory, int threadsNumber) { ConcurrentKafkaListenerContainerFactory factory = diff --git a/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java b/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java index 596a83c8..a4dab743 100644 --- a/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java +++ b/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java @@ -24,5 +24,6 @@ public class KafkaConsumerProperties { private int sourceConcurrency; private int destinationConcurrency; private int withdrawalSessionConcurrency; + private int limitConfigConcurrency; } diff --git a/src/main/java/dev/vality/newway/dao/limiter/LimitConfigDao.java b/src/main/java/dev/vality/newway/dao/limiter/LimitConfigDao.java new file mode 100644 index 00000000..128c1eca --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/limiter/LimitConfigDao.java @@ -0,0 +1,15 @@ +package dev.vality.newway.dao.limiter; + +import dev.vality.dao.GenericDao; +import dev.vality.newway.domain.tables.pojos.LimitConfig; +import dev.vality.newway.exception.DaoException; + +import java.util.Optional; + +public interface LimitConfigDao extends GenericDao { + + Optional save(LimitConfig limitConfig) throws DaoException; + + void updateNotCurrent(Long id) throws DaoException; + +} diff --git a/src/main/java/dev/vality/newway/dao/limiter/LimitConfigDaoImpl.java b/src/main/java/dev/vality/newway/dao/limiter/LimitConfigDaoImpl.java new file mode 100644 index 00000000..87fc02ad --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/limiter/LimitConfigDaoImpl.java @@ -0,0 +1,49 @@ +package dev.vality.newway.dao.limiter; + +import dev.vality.dao.impl.AbstractGenericDao; +import dev.vality.mapper.RecordRowMapper; +import dev.vality.newway.domain.tables.pojos.LimitConfig; +import dev.vality.newway.exception.DaoException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.Optional; + +import static dev.vality.newway.domain.Tables.LIMIT_CONFIG; + +@Component +public class LimitConfigDaoImpl extends AbstractGenericDao implements LimitConfigDao { + + private final RowMapper limitConfigRowMapper; + + @Autowired + public LimitConfigDaoImpl(DataSource dataSource) { + super(dataSource); + limitConfigRowMapper = new RecordRowMapper<>(LIMIT_CONFIG, LimitConfig.class); + } + + @Override + public Optional save(LimitConfig limitConfig) throws DaoException { + var limitConfigRecord = getDslContext().newRecord(LIMIT_CONFIG, limitConfig); + var query = getDslContext() + .insertInto(LIMIT_CONFIG) + .set(limitConfigRecord) + .onConflict(LIMIT_CONFIG.LIMIT_CONFIG_ID, LIMIT_CONFIG.SEQUENCE_ID) + .doNothing() + .returning(LIMIT_CONFIG.ID); + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + execute(query, keyHolder); + return Optional.ofNullable(keyHolder.getKey()).map(Number::longValue); + } + + @Override + public void updateNotCurrent(Long id) throws DaoException { + var query = getDslContext().update(LIMIT_CONFIG).set(LIMIT_CONFIG.CURRENT, false) + .where(LIMIT_CONFIG.ID.eq(id) + .and(LIMIT_CONFIG.CURRENT)); + executeOne(query); + } +} diff --git a/src/main/java/dev/vality/newway/factory/machine/event/LimitConfigMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/LimitConfigMachineEventCopyFactoryImpl.java new file mode 100644 index 00000000..68b79e75 --- /dev/null +++ b/src/main/java/dev/vality/newway/factory/machine/event/LimitConfigMachineEventCopyFactoryImpl.java @@ -0,0 +1,27 @@ +package dev.vality.newway.factory.machine.event; + +import dev.vality.geck.common.util.TypeUtil; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.domain.tables.pojos.LimitConfig; +import org.springframework.stereotype.Component; + +@Component +public class LimitConfigMachineEventCopyFactoryImpl implements MachineEventCopyFactory { + + @Override + public LimitConfig create(MachineEvent event, Long sequenceId, String id, LimitConfig old, String occurredAt) { + var limitConfig = old != null ? new LimitConfig(old) : new LimitConfig(); + limitConfig.setId(null); + limitConfig.setWtime(null); + limitConfig.setSequenceId(sequenceId.intValue()); + limitConfig.setSourceId(id); + limitConfig.setEventCreatedAt(TypeUtil.stringToLocalDateTime(event.getCreatedAt())); + limitConfig.setEventOccuredAt(TypeUtil.stringToLocalDateTime(occurredAt)); + return limitConfig; + } + + @Override + public LimitConfig create(MachineEvent event, Long sequenceId, String id, String occurredAt) { + return create(event, sequenceId, id, null, occurredAt); + } +} diff --git a/src/main/java/dev/vality/newway/handler/event/stock/Handler.java b/src/main/java/dev/vality/newway/handler/event/stock/Handler.java index 6700e665..aa38a869 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/Handler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/Handler.java @@ -1,6 +1,5 @@ package dev.vality.newway.handler.event.stock; - import dev.vality.geck.filter.Filter; import org.apache.commons.lang3.NotImplementedException; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/limiter/LimitConfigCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/limiter/LimitConfigCreatedHandler.java new file mode 100644 index 00000000..b01aa1f0 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/limiter/LimitConfigCreatedHandler.java @@ -0,0 +1,113 @@ +package dev.vality.newway.handler.event.stock.impl.limiter; + +import dev.vality.geck.common.util.TBaseUtil; +import dev.vality.geck.common.util.TypeUtil; +import dev.vality.geck.filter.Filter; +import dev.vality.geck.filter.PathConditionFilter; +import dev.vality.geck.filter.condition.IsNullCondition; +import dev.vality.geck.filter.rule.PathConditionRule; +import dev.vality.limiter.config.LimitScopeType; +import dev.vality.limiter.config.TimestampedChange; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.dao.limiter.LimitConfigDao; +import dev.vality.newway.domain.enums.*; +import dev.vality.newway.domain.tables.pojos.LimitConfig; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; +import dev.vality.newway.util.JsonUtil; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +@Component +@RequiredArgsConstructor +public class LimitConfigCreatedHandler implements LimitConfigHandler { + + private final LimitConfigDao limitConfigDao; + private final MachineEventCopyFactory limitConfigMachineEventCopyFactory; + + @Getter + private final Filter filter = new PathConditionFilter(new PathConditionRule( + "change.created", + new IsNullCondition().not())); + + @Override + @Transactional(propagation = Propagation.REQUIRED) + public void handle(TimestampedChange timestampedChange, MachineEvent event) { + var change = timestampedChange.getChange(); + var limitConfigSource = change.getCreated().getLimitConfig(); + var limitConfigId = limitConfigSource.getId(); + var sequenceId = event.getEventId(); + var sourceId = event.getSourceId(); + log.info("Start LimitConfig created handling, sequenceId={}, limitConfigId={}", sequenceId, limitConfigId); + var limitConfig = limitConfigMachineEventCopyFactory.create( + event, sequenceId, sourceId, timestampedChange.getOccuredAt()); + limitConfig.setLimitConfigId(limitConfigId); + limitConfig.setProcessorType(limitConfigSource.getProcessorType()); + limitConfig.setCreatedAt(TypeUtil.stringToLocalDateTime(limitConfigSource.getCreatedAt())); + limitConfig.setStartedAt(TypeUtil.stringToLocalDateTime(limitConfigSource.getStartedAt())); + limitConfig.setShardSize(limitConfigSource.getShardSize()); + if (limitConfigSource.isSetTimeRangeType()) { + limitConfig.setTimeRangeType(TBaseUtil.unionFieldToEnum( + limitConfigSource.getTimeRangeType(), LimitConfigTimeRangeType.class)); + switch (limitConfigSource.getTimeRangeType().getSetField()) { + case CALENDAR -> limitConfig.setTimeRangeTypeCalendar(TBaseUtil.unionFieldToEnum( + limitConfigSource.getTimeRangeType().getCalendar(), + LimitConfigTimeRangeTypeCalendar.class)); + case INTERVAL -> limitConfig.setTimeRangeTypeIntervalAmount( + limitConfigSource.getTimeRangeType().getInterval().getAmount()); + } + } + if (limitConfigSource.isSetContextType()) { + limitConfig.setLimitContextType(TBaseUtil.unionFieldToEnum( + limitConfigSource.getContextType(), LimitConfigLimitContextType.class)); + } + if (limitConfigSource.isSetType()) { + if (limitConfigSource.getType().isSetTurnover()) { + var turnover = limitConfigSource.getType().getTurnover(); + if (turnover.isSetMetric()) { + var metric = turnover.getMetric(); + limitConfig.setLimitTypeTurnoverMetric( + TBaseUtil.unionFieldToEnum(metric, LimitConfigLimitTypeTurnoverMetric.class)); + if (metric.isSetAmount()) { + limitConfig.setLimitTypeTurnoverMetricAmountCurrency(metric.getAmount().getCurrency()); + } + } + } + } + if (limitConfigSource.isSetScope()) { + limitConfig.setLimitScope(TBaseUtil.unionFieldToEnum( + limitConfigSource.getScope(), LimitConfigLimitScope.class)); + switch (limitConfigSource.getScope().getSetField()) { + case SINGLE -> limitConfig.setLimitScopeTypesJson( + getLimitScopeTypesJson(Set.of(limitConfigSource.getScope().getSingle()))); + case MULTI -> limitConfig.setLimitScopeTypesJson( + getLimitScopeTypesJson(limitConfigSource.getScope().getMulti())); + } + } + limitConfig.setDescription(limitConfigSource.getDescription()); + if (limitConfigSource.isSetOpBehaviour() && limitConfigSource.getOpBehaviour().isSetInvoicePaymentRefund()) { + limitConfig.setOperationLimitBehaviour(TBaseUtil.unionFieldToEnum( + limitConfigSource.getOpBehaviour().getInvoicePaymentRefund(), + LimitConfigOperationLimitBehaviour.class)); + } + + limitConfigDao.save(limitConfig).ifPresentOrElse( + dbContractId -> log.info("LimitConfig created has been saved, sequenceId={}, limitConfigId={}", + sequenceId, limitConfigId), + () -> log.info("LimitConfig created bound duplicated, sequenceId={}, limitConfigId={}", + sequenceId, limitConfigId)); + } + + private String getLimitScopeTypesJson(Set limitScopeTypes) { + return JsonUtil.objectToJsonString(limitScopeTypes.stream() + .map(JsonUtil::thriftBaseToJsonNode) + .collect(Collectors.toList())); + } +} diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/limiter/LimitConfigHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/limiter/LimitConfigHandler.java new file mode 100644 index 00000000..504b5d53 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/limiter/LimitConfigHandler.java @@ -0,0 +1,8 @@ +package dev.vality.newway.handler.event.stock.impl.limiter; + +import dev.vality.limiter.config.TimestampedChange; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.handler.event.stock.Handler; + +public interface LimitConfigHandler extends Handler { +} diff --git a/src/main/java/dev/vality/newway/listener/LimitConfigKafkaListener.java b/src/main/java/dev/vality/newway/listener/LimitConfigKafkaListener.java new file mode 100644 index 00000000..26cb18e9 --- /dev/null +++ b/src/main/java/dev/vality/newway/listener/LimitConfigKafkaListener.java @@ -0,0 +1,36 @@ +package dev.vality.newway.listener; + +import dev.vality.kafka.common.util.LogUtil; +import dev.vality.machinegun.eventsink.SinkEvent; +import dev.vality.newway.service.LimitConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.support.Acknowledgment; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@RequiredArgsConstructor +@Service +public class LimitConfigKafkaListener { + + private final LimitConfigService limitConfigService; + + @KafkaListener( + autoStartup = "${kafka.topics.limit-config.enabled}", + topics = "${kafka.topics.limit-config.id}", + containerFactory = "limitConfigContainerFactory") + public void handle(List> messages, Acknowledgment ack) { + log.info("Got machineEvent batch with size: {}", messages.size()); + limitConfigService.handleEvents(messages.stream() + .map(m -> m.value().getEvent()) + .collect(Collectors.toList())); + ack.acknowledge(); + log.info("Batch has been committed, size={}, {}", messages.size(), + LogUtil.toSummaryStringWithSinkEventValues(messages)); + } +} diff --git a/src/main/java/dev/vality/newway/serde/LimitConfigChangeMachineEventParser.java b/src/main/java/dev/vality/newway/serde/LimitConfigChangeMachineEventParser.java new file mode 100644 index 00000000..2c4cc1dc --- /dev/null +++ b/src/main/java/dev/vality/newway/serde/LimitConfigChangeMachineEventParser.java @@ -0,0 +1,14 @@ +package dev.vality.newway.serde; + +import dev.vality.limiter.config.TimestampedChange; +import dev.vality.sink.common.parser.impl.MachineEventParser; +import dev.vality.sink.common.serialization.BinaryDeserializer; +import org.springframework.stereotype.Service; + +@Service +public class LimitConfigChangeMachineEventParser extends MachineEventParser { + + public LimitConfigChangeMachineEventParser(BinaryDeserializer deserializer) { + super(deserializer); + } +} diff --git a/src/main/java/dev/vality/newway/serde/deserializer/LimitConfigChangeDeserializer.java b/src/main/java/dev/vality/newway/serde/deserializer/LimitConfigChangeDeserializer.java new file mode 100644 index 00000000..9ed0744e --- /dev/null +++ b/src/main/java/dev/vality/newway/serde/deserializer/LimitConfigChangeDeserializer.java @@ -0,0 +1,14 @@ +package dev.vality.newway.serde.deserializer; + +import dev.vality.limiter.config.TimestampedChange; +import dev.vality.sink.common.serialization.impl.AbstractThriftBinaryDeserializer; +import org.springframework.stereotype.Service; + +@Service +public class LimitConfigChangeDeserializer extends AbstractThriftBinaryDeserializer { + + @Override + public TimestampedChange deserialize(byte[] bin) { + return deserialize(bin, new TimestampedChange()); + } +} diff --git a/src/main/java/dev/vality/newway/service/LimitConfigService.java b/src/main/java/dev/vality/newway/service/LimitConfigService.java new file mode 100644 index 00000000..00cb2c5f --- /dev/null +++ b/src/main/java/dev/vality/newway/service/LimitConfigService.java @@ -0,0 +1,36 @@ +package dev.vality.newway.service; + +import dev.vality.limiter.config.TimestampedChange; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.handler.event.stock.impl.limiter.LimitConfigHandler; +import dev.vality.sink.common.parser.impl.MachineEventParser; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class LimitConfigService { + + private final List handlers; + private final MachineEventParser parser; + + @Transactional(propagation = Propagation.REQUIRED) + public void handleEvents(List machineEvents) { + machineEvents.forEach(this::handleIfAccept); + } + + private void handleIfAccept(MachineEvent machineEvent) { + TimestampedChange eventPayload = parser.parse(machineEvent); + if (eventPayload.isSetChange()) { + handlers.stream() + .filter(handler -> handler.accept(eventPayload)) + .forEach(handler -> handler.handle(eventPayload, machineEvent)); + } + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b6e1d120..1ac43272 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -66,6 +66,7 @@ kafka: source-concurrency: 7 destination-concurrency: 7 withdrawal-session-concurrency: 7 + limit-config-concurrency: 7 topics: invoice: id: mg-invoice-100-2 @@ -104,6 +105,9 @@ kafka: pm-events-payout: id: pm-events-payout enabled: false + limit-config: + id: mg-events-lim-config + enabled: false dmt: url: http://dominant:8022/v1/domain/repository diff --git a/src/main/resources/db/migration/V3__add_limit_config.sql b/src/main/resources/db/migration/V3__add_limit_config.sql new file mode 100644 index 00000000..f5d147bc --- /dev/null +++ b/src/main/resources/db/migration/V3__add_limit_config.sql @@ -0,0 +1,35 @@ +create type dw.limit_config_time_range_type as enum ('calendar', 'interval'); +create type dw.limit_config_time_range_type_calendar as enum ('year', 'month', 'week', 'day'); +create type dw.limit_config_limit_context_type as enum ('payment_processing', 'withdrawal_processing'); +create type dw.limit_config_limit_type_turnover_metric as enum ('number', 'amount'); +create type dw.limit_config_limit_scope as enum ('multi', 'single'); +create type dw.limit_config_limit_scope_type as enum ('party', 'shop', 'wallet', 'identity', 'payment_tool'); +create type dw.limit_config_operation_limit_behaviour as enum ('subtraction', 'addition'); + +create table if not exists dw.limit_config +( + id bigserial not null, + source_id varchar not null, + sequence_id int not null, + event_occured_at timestamp not null, + event_created_at timestamp not null, + limit_config_id varchar not null, + processor_type varchar not null, + created_at timestamp not null, + started_at timestamp not null, + shard_size bigint not null, + time_range_type dw.limit_config_time_range_type not null, + time_range_type_calendar dw.limit_config_time_range_type_calendar, + time_range_type_interval_amount bigint, + limit_context_type dw.limit_config_limit_context_type not null, + limit_type_turnover_metric dw.limit_config_limit_type_turnover_metric, + limit_type_turnover_metric_amount_currency varchar, + limit_scope dw.limit_config_limit_scope, + limit_scope_types_json text, + description varchar, + operation_limit_behaviour dw.limit_config_operation_limit_behaviour, + wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc'), + current BOOLEAN NOT NULL DEFAULT TRUE, + constraint limit_config_id_pkey primary key (id), + constraint limit_config_limit_config_id_ukey unique (limit_config_id, sequence_id) +); \ No newline at end of file diff --git a/src/test/java/dev/vality/newway/config/KafkaPostgresqlSpringBootITest.java b/src/test/java/dev/vality/newway/config/KafkaPostgresqlSpringBootITest.java index 292f3ee6..21722a42 100644 --- a/src/test/java/dev/vality/newway/config/KafkaPostgresqlSpringBootITest.java +++ b/src/test/java/dev/vality/newway/config/KafkaPostgresqlSpringBootITest.java @@ -27,7 +27,8 @@ "kafka.topics.withdrawal-session.enabled=true", "kafka.topics.source.enabled=true", "kafka.topics.destination.enabled=true", - "kafka.topics.pm-events-payout.enabled=true"}, + "kafka.topics.pm-events-payout.enabled=true", + "kafka.topics.limit-config.enabled=true"}, topicsKeys = { "kafka.topics.invoice.id", "kafka.topics.recurrent-payment-tool.id", @@ -40,7 +41,8 @@ "kafka.topics.withdrawal-session.id", "kafka.topics.source.id", "kafka.topics.destination.id", - "kafka.topics.pm-events-payout.id"}) + "kafka.topics.pm-events-payout.id", + "kafka.topics.limit-config.id"}) @DefaultSpringBootTest @Import(KafkaProducer.class) public @interface KafkaPostgresqlSpringBootITest { diff --git a/src/test/java/dev/vality/newway/dao/LimitConfigDaoTest.java b/src/test/java/dev/vality/newway/dao/LimitConfigDaoTest.java new file mode 100644 index 00000000..5c45d11e --- /dev/null +++ b/src/test/java/dev/vality/newway/dao/LimitConfigDaoTest.java @@ -0,0 +1,60 @@ +package dev.vality.newway.dao; + +import dev.vality.limiter.config.LimitScopeType; +import dev.vality.mapper.RecordRowMapper; +import dev.vality.newway.config.PostgresqlSpringBootITest; +import dev.vality.newway.dao.limiter.LimitConfigDao; +import dev.vality.newway.domain.tables.pojos.LimitConfig; +import dev.vality.newway.util.JsonUtil; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.util.Set; +import java.util.stream.Collectors; + +import static dev.vality.newway.domain.tables.LimitConfig.LIMIT_CONFIG; +import static dev.vality.newway.utils.LimitConfigGenerator.getLimitConfig; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@PostgresqlSpringBootITest +public class LimitConfigDaoTest { + + public static final String SELECT_CURRENT = "select * from dw.limit_config where limit_config_id = ? and current = true;"; + + @Autowired + private JdbcTemplate jdbcTemplate; + + @Autowired + private LimitConfigDao limitConfigDao; + + @Test + public void limitConfigDaoTest() { + var pojo = dev.vality.testcontainers.annotations.util.RandomBeans.random(LimitConfig.class); + pojo.setCurrent(true); + pojo.setLimitScopeTypesJson(getLimitScopeTypesJson(getLimitConfig(pojo.getLimitConfigId()).getScope().getMulti())); + var id = limitConfigDao.save(pojo).get(); + pojo.setId(id); + var limitConfigId = pojo.getLimitConfigId(); + var actual = selectCurrent(limitConfigId); + assertEquals(pojo, actual); + limitConfigDao.updateNotCurrent(actual.getId()); + limitConfigDao.save(pojo); + assertThrows(EmptyResultDataAccessException.class, () -> selectCurrent(pojo.getLimitConfigId())); + } + + private LimitConfig selectCurrent(String limitConfigId) { + return jdbcTemplate.queryForObject( + SELECT_CURRENT, + new RecordRowMapper<>(LIMIT_CONFIG, LimitConfig.class), + limitConfigId); + } + + private String getLimitScopeTypesJson(Set limitScopeTypes) { + return JsonUtil.objectToJsonString(limitScopeTypes.stream() + .map(JsonUtil::thriftBaseToJsonNode) + .collect(Collectors.toList())); + } +} diff --git a/src/test/java/dev/vality/newway/kafka/KafkaProducer.java b/src/test/java/dev/vality/newway/kafka/KafkaProducer.java index 1b3ceb06..505161cd 100644 --- a/src/test/java/dev/vality/newway/kafka/KafkaProducer.java +++ b/src/test/java/dev/vality/newway/kafka/KafkaProducer.java @@ -24,7 +24,6 @@ public class KafkaProducer { public void sendMessage(String topic) { SinkEvent sinkEvent = new SinkEvent(); sinkEvent.setEvent(createMessage()); - testThriftKafkaProducer.send(topic, sinkEvent); } diff --git a/src/test/java/dev/vality/newway/kafka/LimitConfigKafkaListenerTest.java b/src/test/java/dev/vality/newway/kafka/LimitConfigKafkaListenerTest.java new file mode 100644 index 00000000..9839aa7f --- /dev/null +++ b/src/test/java/dev/vality/newway/kafka/LimitConfigKafkaListenerTest.java @@ -0,0 +1,33 @@ +package dev.vality.newway.kafka; + +import dev.vality.newway.config.KafkaPostgresqlSpringBootITest; +import dev.vality.newway.service.LimitConfigService; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.mock.mockito.MockBean; + +import java.util.concurrent.TimeUnit; + +import static org.mockito.ArgumentMatchers.anyList; + +@KafkaPostgresqlSpringBootITest +public class LimitConfigKafkaListenerTest { + + @Value("${kafka.topics.limit-config.id}") + public String topic; + + @Autowired + private KafkaProducer kafkaProducer; + + @MockBean + private LimitConfigService limitConfigService; + + @Test + public void listenEmptyChanges() { + kafkaProducer.sendMessage(topic); + Mockito.verify(limitConfigService, Mockito.timeout(TimeUnit.MINUTES.toMillis(1)).times(1)) + .handleEvents(anyList()); + } +} diff --git a/src/test/java/dev/vality/newway/service/LimitConfigServiceTest.java b/src/test/java/dev/vality/newway/service/LimitConfigServiceTest.java new file mode 100644 index 00000000..c3f82607 --- /dev/null +++ b/src/test/java/dev/vality/newway/service/LimitConfigServiceTest.java @@ -0,0 +1,75 @@ +package dev.vality.newway.service; + +import dev.vality.limiter.config.LimitConfig; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.mapper.RecordRowMapper; +import dev.vality.newway.config.PostgresqlSpringBootITest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.test.jdbc.JdbcTestUtils; + +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import static dev.vality.newway.dao.LimitConfigDaoTest.SELECT_CURRENT; +import static dev.vality.newway.domain.tables.LimitConfig.LIMIT_CONFIG; +import static dev.vality.newway.utils.LimitConfigGenerator.*; +import static org.assertj.core.api.Assertions.assertThat; + +@PostgresqlSpringBootITest +public class LimitConfigServiceTest { + + private static final String TABLE_NAME = LIMIT_CONFIG.getSchema().getName() + "." + LIMIT_CONFIG.getName(); + + @Autowired + private LimitConfigService limitConfigService; + + @Autowired + private JdbcTemplate jdbcTemplate; + + @Test + public void shouldHandleAndSave() { + var limitConfigId = UUID.randomUUID().toString(); + var limitConfig = getLimitConfig(limitConfigId); + limitConfigService.handleEvents(List.of( + getMachineEvent(limitConfigId, limitConfig), getMachineEvent(UUID.randomUUID().toString()))); + assertThat(JdbcTestUtils.countRowsInTable(jdbcTemplate, TABLE_NAME)) + .isEqualTo(2); + var saved = selectCurrent(limitConfigId); + assertThat(saved.getShardSize()) + .isEqualTo(limitConfig.getShardSize()); + } + + @Test + public void shouldHandleAndSaveWithZeroScope() { + var limitConfigId = UUID.randomUUID().toString(); + var limitConfig = getLimitConfig(limitConfigId); + limitConfig.getScope().setMulti(Set.of()); + limitConfigService.handleEvents(List.of(getMachineEvent(limitConfigId, limitConfig))); + assertThat(JdbcTestUtils.countRowsInTable(jdbcTemplate, TABLE_NAME)) + .isEqualTo(1); + var saved = selectCurrent(limitConfigId); + assertThat(saved.getShardSize()) + .isEqualTo(limitConfig.getShardSize()); + } + + private dev.vality.newway.domain.tables.pojos.LimitConfig selectCurrent(String limitConfigId) { + return jdbcTemplate.queryForObject( + SELECT_CURRENT, + new RecordRowMapper<>(LIMIT_CONFIG, dev.vality.newway.domain.tables.pojos.LimitConfig.class), + limitConfigId); + } + + private MachineEvent getMachineEvent(String limitConfigId) { + return getMachineEvent(limitConfigId, getLimitConfig(limitConfigId)); + } + + private MachineEvent getMachineEvent(String limitConfigId, LimitConfig limitConfig) { + return getEvent( + limitConfigId, + 1, + getCreated(limitConfig)); + } +} diff --git a/src/test/java/dev/vality/newway/utils/LimitConfigGenerator.java b/src/test/java/dev/vality/newway/utils/LimitConfigGenerator.java new file mode 100644 index 00000000..0a74ff2f --- /dev/null +++ b/src/test/java/dev/vality/newway/utils/LimitConfigGenerator.java @@ -0,0 +1,67 @@ +package dev.vality.newway.utils; + +import dev.vality.geck.common.util.TypeUtil; +import dev.vality.limiter.config.*; +import dev.vality.machinegun.eventsink.MachineEvent; +import lombok.SneakyThrows; +import org.apache.thrift.TBase; +import org.apache.thrift.TSerializer; +import org.apache.thrift.protocol.TBinaryProtocol; + +import java.time.Instant; +import java.util.List; +import java.util.Set; + +import static dev.vality.testcontainers.annotations.util.RandomBeans.randomThriftOnlyRequiredFields; + +public class LimitConfigGenerator { + + public static List getEvents( + String sourceId, + long sequenceId, + Change change) { + return List.of(getEvent(sourceId, sequenceId, change)); + } + + public static MachineEvent getEvent( + String sourceId, + long sequenceId, + Change change) { + return new MachineEvent() + .setData(toByteArray(new TimestampedChange() + .setChange(change) + .setOccuredAt(getTemporal()))) + .setCreatedAt(Instant.now().toString()) + .setEventId(sequenceId) + .setSourceNs("source_ns") + .setSourceId(sourceId); + } + + public static Change getCreated(LimitConfig limitConfig) { + return Change.created(new CreatedChange(limitConfig)); + } + + public static LimitConfig getLimitConfig(String limitConfigId) { + return randomThriftOnlyRequiredFields(LimitConfig.class) + .setId(limitConfigId) + .setType(LimitType.turnover(new LimitTypeTurnover().setMetric(LimitTurnoverMetric.amount(new LimitTurnoverAmount("RUB"))))) + .setScope(LimitScope.multi(Set.of(LimitScopeType.identity(new LimitScopeEmptyDetails()), + LimitScopeType.party(new LimitScopeEmptyDetails()), + LimitScopeType.shop(new LimitScopeEmptyDetails())))) + .setDescription("asd") + .setCreatedAt(getTemporal()) + .setStartedAt(getTemporal()) + .setOpBehaviour(new OperationLimitBehaviour().setInvoicePaymentRefund(OperationBehaviour.addition(new Addition()))); + } + + @SneakyThrows + private static dev.vality.machinegun.msgpack.Value toByteArray(TBase thrift) { + return dev.vality.machinegun.msgpack.Value.bin( + new TSerializer(new TBinaryProtocol.Factory()) + .serialize(thrift)); + } + + private static String getTemporal() { + return TypeUtil.temporalToString(Instant.now()); + } +} From 624317f1a193f0c993b40a01fd347852db98e93c Mon Sep 17 00:00:00 2001 From: struga Date: Thu, 15 Sep 2022 10:12:21 +0300 Subject: [PATCH 09/36] Remove secure data columns (#62) --- .../vality/newway/handler/dominant/impl/ProviderHandler.java | 1 - .../dev/vality/newway/handler/dominant/impl/ProxyHandler.java | 1 - .../vality/newway/handler/dominant/impl/TerminalHandler.java | 3 --- .../resources/db/migration/V4__drop_column_with_sec_data.sql | 4 ++++ 4 files changed, 4 insertions(+), 5 deletions(-) create mode 100644 src/main/resources/db/migration/V4__drop_column_with_sec_data.sql diff --git a/src/main/java/dev/vality/newway/handler/dominant/impl/ProviderHandler.java b/src/main/java/dev/vality/newway/handler/dominant/impl/ProviderHandler.java index 805045ae..85c6f782 100644 --- a/src/main/java/dev/vality/newway/handler/dominant/impl/ProviderHandler.java +++ b/src/main/java/dev/vality/newway/handler/dominant/impl/ProviderHandler.java @@ -49,7 +49,6 @@ public Provider convertToDatabaseObject(ProviderObject providerObject, Long vers provider.setName(data.getName()); provider.setDescription(data.getDescription()); provider.setProxyRefId(data.getProxy().getRef().getId()); - provider.setProxyAdditionalJson(JsonUtil.objectToJsonString(data.getProxy().getAdditional())); if (data.isSetAbsAccount()) { provider.setAbsAccount(data.getAbsAccount()); } diff --git a/src/main/java/dev/vality/newway/handler/dominant/impl/ProxyHandler.java b/src/main/java/dev/vality/newway/handler/dominant/impl/ProxyHandler.java index cdbb114a..8fa494fd 100644 --- a/src/main/java/dev/vality/newway/handler/dominant/impl/ProxyHandler.java +++ b/src/main/java/dev/vality/newway/handler/dominant/impl/ProxyHandler.java @@ -47,7 +47,6 @@ public Proxy convertToDatabaseObject(ProxyObject proxyObject, Long versionId, bo proxy.setName(data.getName()); proxy.setDescription(data.getDescription()); proxy.setUrl(data.getUrl()); - proxy.setOptionsJson(JsonUtil.objectToJsonString(data.getOptions())); proxy.setCurrent(current); return proxy; } diff --git a/src/main/java/dev/vality/newway/handler/dominant/impl/TerminalHandler.java b/src/main/java/dev/vality/newway/handler/dominant/impl/TerminalHandler.java index 6247f92d..2c1025b1 100644 --- a/src/main/java/dev/vality/newway/handler/dominant/impl/TerminalHandler.java +++ b/src/main/java/dev/vality/newway/handler/dominant/impl/TerminalHandler.java @@ -45,9 +45,6 @@ public Terminal convertToDatabaseObject(TerminalObject terminalObject, Long vers dev.vality.damsel.domain.Terminal data = terminalObject.getData(); terminal.setName(data.getName()); terminal.setDescription(data.getDescription()); - if (data.isSetOptions()) { - terminal.setOptionsJson(JsonUtil.objectToJsonString(data.getOptions())); - } if (data.isSetRiskCoverage()) { terminal.setRiskCoverage(data.getRiskCoverage().name()); } diff --git a/src/main/resources/db/migration/V4__drop_column_with_sec_data.sql b/src/main/resources/db/migration/V4__drop_column_with_sec_data.sql new file mode 100644 index 00000000..3b00649b --- /dev/null +++ b/src/main/resources/db/migration/V4__drop_column_with_sec_data.sql @@ -0,0 +1,4 @@ +ALTER TABLE dw.terminal DROP COLUMN options_json; +ALTER TABLE dw.provider DROP COLUMN proxy_additional_json; +ALTER TABLE dw.withdrawal_provider DROP COLUMN proxy_additional_json; +ALTER TABLE dw.proxy DROP COLUMN options_json; \ No newline at end of file From 9353690ef536d1ca921f7e778fcb9a64956d48e1 Mon Sep 17 00:00:00 2001 From: Inal Arsanukaev Date: Tue, 20 Sep 2022 16:45:38 +0300 Subject: [PATCH 10/36] Removed null setting for party revision --- .../factory/machine/event/PartyMachineEventCopyFactoryImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/dev/vality/newway/factory/machine/event/PartyMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/PartyMachineEventCopyFactoryImpl.java index bcbb8df2..dc7b1d54 100644 --- a/src/main/java/dev/vality/newway/factory/machine/event/PartyMachineEventCopyFactoryImpl.java +++ b/src/main/java/dev/vality/newway/factory/machine/event/PartyMachineEventCopyFactoryImpl.java @@ -18,7 +18,6 @@ public Party create(MachineEvent event, Long sequenceId, Integer id, Party old, party = new Party(); } party.setId(null); - party.setRevision(null); party.setWtime(null); party.setSequenceId(sequenceId.intValue()); party.setChangeId(id); From f295a28c32d6635d7f47968df7026ced2e1418e6 Mon Sep 17 00:00:00 2001 From: struga Date: Thu, 22 Sep 2022 11:00:37 +0300 Subject: [PATCH 11/36] Add term id to daway (#65) --- .../event/stock/impl/withdrawal/WithdrawalCreatedHandler.java | 3 +++ .../stock/impl/withdrawal/WithdrawalRouteChangeHandler.java | 2 ++ .../resources/db/migration/V5__add_terminal_id_withdrawal.sql | 1 + 3 files changed, 6 insertions(+) create mode 100644 src/main/resources/db/migration/V5__add_terminal_id_withdrawal.sql diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalCreatedHandler.java index 10f4faaa..44cec14d 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalCreatedHandler.java @@ -48,6 +48,9 @@ public void handle(TimestampedChange timestampedChange, MachineEvent event) { withdrawal.setAmount(cash.getAmount()); withdrawal.setCurrencyCode(cash.getCurrency().getSymbolicCode()); withdrawal.setWithdrawalStatus(WithdrawalStatus.pending); + if (withdrawalDamsel.getRoute() != null && withdrawalDamsel.getRoute().isSetTerminalId()) { + withdrawal.setTerminalId(String.valueOf(withdrawalDamsel.getRoute().getTerminalId())); + } withdrawalDao.save(withdrawal).ifPresentOrElse( dbContractId -> log diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalRouteChangeHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalRouteChangeHandler.java index 1516eeba..f828be47 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalRouteChangeHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalRouteChangeHandler.java @@ -54,6 +54,8 @@ public void handle(TimestampedChange timestampedChange, MachineEvent event) { String providerIdLegacy = route.getProviderIdLegacy(); withdrawalNew.setProviderId(providerId); withdrawalNew.setProviderIdLegacy(providerIdLegacy); + withdrawalNew.setTerminalId(route.isSetTerminalId() + ? String.valueOf(route.getTerminalId()) : null); withdrawalDao.save(withdrawalNew).ifPresentOrElse( id -> { diff --git a/src/main/resources/db/migration/V5__add_terminal_id_withdrawal.sql b/src/main/resources/db/migration/V5__add_terminal_id_withdrawal.sql new file mode 100644 index 00000000..76db4627 --- /dev/null +++ b/src/main/resources/db/migration/V5__add_terminal_id_withdrawal.sql @@ -0,0 +1 @@ +ALTER TABLE dw.withdrawal ADD COLUMN IF NOT EXISTS terminal_id character varying; \ No newline at end of file From a24a3a54d7efd22885858fdf913c406c7de89267 Mon Sep 17 00:00:00 2001 From: Gregory <32060161+ggmaleva@users.noreply.github.com> Date: Mon, 26 Sep 2022 19:36:40 +0300 Subject: [PATCH 12/36] fix npe with key (#68) Co-authored-by: ggmaleva --- .../PaymentAdditionalInfoWrapperHandler.java | 8 +++-- .../payment/PaymentFeeWrapperHandler.java | 8 +++-- .../PaymentRecurrentInfoWrapperHandler.java | 8 +++-- .../PaymentRiskDataWrapperHandler.java | 10 ++++--- .../payment/PaymentRouteWrapperHandler.java | 8 +++-- .../PaymentStatusInfoWrapperHandler.java | 8 +++-- .../InvoicePaymentCaptureStartedMapper.java | 2 +- .../service/PaymentWrapperServiceTest.java | 29 +++++++++++++++++-- 8 files changed, 58 insertions(+), 23 deletions(-) diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentAdditionalInfoWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentAdditionalInfoWrapperHandler.java index 0f14e70d..b7d2c73a 100644 --- a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentAdditionalInfoWrapperHandler.java +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentAdditionalInfoWrapperHandler.java @@ -27,11 +27,13 @@ public boolean accept(List wrappers) { @Override public void saveBatch(List wrappers) { - List paymentAdditionalInfos = wrappers.stream() + List processableWrappers = wrappers.stream() + .filter(paymentWrapper -> Objects.nonNull(paymentWrapper.getPaymentAdditionalInfo())) + .collect(Collectors.toList()); + List paymentAdditionalInfos = processableWrappers.stream() .map(PaymentWrapper::getPaymentAdditionalInfo) - .filter(Objects::nonNull) .collect(Collectors.toList()); paymentAdditionalInfoDao.saveBatch(paymentAdditionalInfos); - paymentAdditionalInfoDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(wrappers)); + paymentAdditionalInfoDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(processableWrappers)); } } diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentFeeWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentFeeWrapperHandler.java index 79b5fea8..8feb05bd 100644 --- a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentFeeWrapperHandler.java +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentFeeWrapperHandler.java @@ -27,11 +27,13 @@ public boolean accept(List wrappers) { @Override public void saveBatch(List wrappers) { - List paymentFees = wrappers.stream() + List processableWrappers = wrappers.stream() + .filter(paymentWrapper -> Objects.nonNull(paymentWrapper.getPaymentFee())) + .collect(Collectors.toList()); + List paymentFees = processableWrappers.stream() .map(PaymentWrapper::getPaymentFee) - .filter(Objects::nonNull) .collect(Collectors.toList()); paymentFeeDao.saveBatch(paymentFees); - paymentFeeDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(wrappers)); + paymentFeeDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(processableWrappers)); } } diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRecurrentInfoWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRecurrentInfoWrapperHandler.java index 416cc4c9..bae7f7de 100644 --- a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRecurrentInfoWrapperHandler.java +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRecurrentInfoWrapperHandler.java @@ -27,11 +27,13 @@ public boolean accept(List wrappers) { @Override public void saveBatch(List wrappers) { - List paymentRecurrentInfos = wrappers.stream() + List processableWrappers = wrappers.stream() + .filter(paymentWrapper -> Objects.nonNull(paymentWrapper.getPaymentRecurrentInfo())) + .collect(Collectors.toList()); + List paymentRecurrentInfos = processableWrappers.stream() .map(PaymentWrapper::getPaymentRecurrentInfo) - .filter(Objects::nonNull) .collect(Collectors.toList()); paymentRecurrentInfoDao.saveBatch(paymentRecurrentInfos); - paymentRecurrentInfoDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(wrappers)); + paymentRecurrentInfoDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(processableWrappers)); } } diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRiskDataWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRiskDataWrapperHandler.java index 02c79367..f1177676 100644 --- a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRiskDataWrapperHandler.java +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRiskDataWrapperHandler.java @@ -27,11 +27,13 @@ public boolean accept(List wrappers) { @Override public void saveBatch(List wrappers) { - List paymentRiskDataList = wrappers.stream() + List processableWrappers = wrappers.stream() + .filter(paymentWrapper -> Objects.nonNull(paymentWrapper.getPaymentRiskData())) + .collect(Collectors.toList()); + List paymentRiskDataList = processableWrappers.stream() .map(PaymentWrapper::getPaymentRiskData) - .filter(Objects::nonNull) .collect(Collectors.toList()); - paymentRiskDataDao.saveBatch(paymentRiskDataList); - paymentRiskDataDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(wrappers)); + paymentRiskDataDao.saveBatch(paymentRiskDataList); + paymentRiskDataDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(processableWrappers)); } } diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRouteWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRouteWrapperHandler.java index 873803f1..9d52c2d0 100644 --- a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRouteWrapperHandler.java +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentRouteWrapperHandler.java @@ -27,11 +27,13 @@ public boolean accept(List wrappers) { @Override public void saveBatch(List wrappers) { - List paymentRoutes = wrappers.stream() + List processableWrappers = wrappers.stream() + .filter(paymentWrapper -> Objects.nonNull(paymentWrapper.getPaymentRoute())) + .collect(Collectors.toList()); + List paymentRoutes = processableWrappers.stream() .map(PaymentWrapper::getPaymentRoute) - .filter(Objects::nonNull) .collect(Collectors.toList()); paymentRouteDao.saveBatch(paymentRoutes); - paymentRouteDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(wrappers)); + paymentRouteDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(processableWrappers)); } } diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentStatusInfoWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentStatusInfoWrapperHandler.java index de891d95..de834608 100644 --- a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentStatusInfoWrapperHandler.java +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentStatusInfoWrapperHandler.java @@ -27,11 +27,13 @@ public boolean accept(List wrappers) { @Override public void saveBatch(List wrappers) { - List paymentStatusInfos = wrappers.stream() + List processableWrappers = wrappers.stream() + .filter(paymentWrapper -> Objects.nonNull(paymentWrapper.getPaymentStatusInfo())) + .collect(Collectors.toList()); + List paymentStatusInfos = processableWrappers.stream() .map(PaymentWrapper::getPaymentStatusInfo) - .filter(Objects::nonNull) .collect(Collectors.toList()); paymentStatusInfoDao.saveBatch(paymentStatusInfos); - paymentStatusInfoDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(wrappers)); + paymentStatusInfoDao.switchCurrent(PaymentWrapperUtil.getInvoicingKeys(processableWrappers)); } } diff --git a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCaptureStartedMapper.java b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCaptureStartedMapper.java index 2c584552..9df31aac 100644 --- a/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCaptureStartedMapper.java +++ b/src/main/java/dev/vality/newway/mapper/payment/InvoicePaymentCaptureStartedMapper.java @@ -32,8 +32,8 @@ public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer chan log.info("Receive InvoicePaymentCaptureStarted sequenceId={}, changeId={}, invoiceId={}, paymentId={}, payload={}", event.getEventId(), changeId, - change.getInvoicePaymentChange().getId(), event.getSourceId(), + change.getInvoicePaymentChange().getId(), change.getInvoicePaymentChange().getPayload().getInvoicePaymentCaptureStarted() ); return new PaymentWrapper(); diff --git a/src/test/java/dev/vality/newway/service/PaymentWrapperServiceTest.java b/src/test/java/dev/vality/newway/service/PaymentWrapperServiceTest.java index 0a3d1046..5dbfceaa 100644 --- a/src/test/java/dev/vality/newway/service/PaymentWrapperServiceTest.java +++ b/src/test/java/dev/vality/newway/service/PaymentWrapperServiceTest.java @@ -1,11 +1,15 @@ package dev.vality.newway.service; +import dev.vality.newway.TestData; import dev.vality.newway.config.PostgresqlSpringBootITest; import dev.vality.newway.dao.invoicing.iface.*; import dev.vality.newway.dao.invoicing.impl.PaymentDaoImpl; import dev.vality.newway.domain.enums.PaymentChangeType; -import dev.vality.newway.domain.tables.pojos.*; +import dev.vality.newway.domain.tables.pojos.CashFlow; +import dev.vality.newway.domain.tables.pojos.CashFlowLink; +import dev.vality.newway.domain.tables.pojos.PaymentFee; import dev.vality.newway.model.CashFlowWrapper; +import dev.vality.newway.model.InvoicingKey; import dev.vality.newway.model.PaymentWrapper; import dev.vality.newway.utils.PaymentWrapperTestUtil; import dev.vality.testcontainers.annotations.util.RandomBeans; @@ -13,11 +17,13 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; +import java.time.LocalDateTime; import java.util.HashSet; import java.util.List; -import static dev.vality.newway.utils.JdbcUtil.countPaymentEntity; import static dev.vality.newway.utils.JdbcUtil.countEntities; +import static dev.vality.newway.utils.JdbcUtil.countPaymentEntity; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @PostgresqlSpringBootITest @@ -77,7 +83,7 @@ public void duplicationTest() { } private List preparePaymentWrappers() { - List paymentWrappers = RandomBeans.randomListOf(2,PaymentWrapper.class); + List paymentWrappers = RandomBeans.randomListOf(2, PaymentWrapper.class); paymentWrappers.forEach(pw -> { pw.setCashFlowWrapper(new CashFlowWrapper( RandomBeans.random(CashFlowLink.class), @@ -131,4 +137,21 @@ private void assertTotalDuplication() { assertEquals(2, countEntities(jdbcTemplate, "cash_flow_link")); assertEquals(6, countEntities(jdbcTemplate, "cash_flow")); } + + @Test + void testPaymentFeeWithEmptyWrapper() { + PaymentFee paymentFee = new PaymentFee(); + paymentFee.setPaymentId(TestData.randomString()); + paymentFee.setInvoiceId(TestData.randomString()); + paymentFee.setEventCreatedAt(LocalDateTime.now()); + PaymentWrapper wrapperWithFee = new PaymentWrapper(); + wrapperWithFee.setPaymentFee(paymentFee); + wrapperWithFee.setKey(InvoicingKey.builder() + .paymentId(paymentFee.getPaymentId()) + .invoiceId(paymentFee.getInvoiceId()) + .build()); + PaymentWrapper emptyWrapper = new PaymentWrapper(); + + assertDoesNotThrow(() -> paymentWrapperService.save(List.of(wrapperWithFee, emptyWrapper))); + } } From f7c66216cec68db1a6cc99de9ef6c66ea8d0bbca Mon Sep 17 00:00:00 2001 From: mr-impossibru <64555470+mr-impossibru@users.noreply.github.com> Date: Tue, 4 Oct 2022 14:49:16 +0300 Subject: [PATCH 13/36] OPS-200: add indices (#69) --- src/main/resources/db/migration/V6__add_indices.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/main/resources/db/migration/V6__add_indices.sql diff --git a/src/main/resources/db/migration/V6__add_indices.sql b/src/main/resources/db/migration/V6__add_indices.sql new file mode 100644 index 00000000..be27adf7 --- /dev/null +++ b/src/main/resources/db/migration/V6__add_indices.sql @@ -0,0 +1,2 @@ +DROP INDEX IF EXISTS payment_status; +CREATE INDEX IF NOT EXISTS payment_status_info_idx ON dw.payment_status_info USING btree (status, event_created_at); \ No newline at end of file From 8f4822e6a529b5e3636b41cf3a4992f77b276c9c Mon Sep 17 00:00:00 2001 From: vitaxa Date: Wed, 19 Oct 2022 10:47:09 +0300 Subject: [PATCH 14/36] add exrate listener (#72) --- .github/workflows/build.yml | 4 +- .github/workflows/deploy.yml | 10 +-- pom.xml | 13 ++- .../dev/vality/newway/config/KafkaConfig.java | 22 ++++- .../properties/KafkaConsumerProperties.java | 1 + .../dao/exrate/iface/ExchangeRateDao.java | 12 +++ .../dao/exrate/impl/ExchangeRateDaoImpl.java | 48 ++++++++++ .../exrate/CurrencyExchangeRateHandler.java | 47 ++++++++++ .../impl/exrate/ExchangeRateHandler.java | 12 +++ .../newway/listener/ExchangeRateListener.java | 36 ++++++++ ...CurrencyExchangeRateEventDeserializer.java | 11 +++ .../newway/service/ExchangeRateService.java | 29 ++++++ src/main/resources/application.yml | 5 ++ .../migration/V7__add_new_exchange_rates.sql | 19 ++++ .../KafkaPostgresqlSpringBootITest.java | 8 +- .../kafka/ExchangeRateKafkaListenerTest.java | 89 +++++++++++++++++++ 16 files changed, 351 insertions(+), 15 deletions(-) create mode 100644 src/main/java/dev/vality/newway/dao/exrate/iface/ExchangeRateDao.java create mode 100644 src/main/java/dev/vality/newway/dao/exrate/impl/ExchangeRateDaoImpl.java create mode 100644 src/main/java/dev/vality/newway/handler/event/stock/impl/exrate/CurrencyExchangeRateHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/event/stock/impl/exrate/ExchangeRateHandler.java create mode 100644 src/main/java/dev/vality/newway/listener/ExchangeRateListener.java create mode 100644 src/main/java/dev/vality/newway/serde/CurrencyExchangeRateEventDeserializer.java create mode 100644 src/main/java/dev/vality/newway/service/ExchangeRateService.java create mode 100644 src/main/resources/db/migration/V7__add_new_exchange_rates.sql create mode 100644 src/test/java/dev/vality/newway/kafka/ExchangeRateKafkaListenerTest.java diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3b310f08..38080b60 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Maven Build Artifact +name: Build Maven Artifact on: pull_request: @@ -7,4 +7,4 @@ on: jobs: build: - uses: valitydev/java-workflow/.github/workflows/maven-service-build.yml@v1 + uses: valitydev/base-workflow/.github/workflows/maven-service-build.yml@v2-beta diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5a405cb6..d464b920 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,4 +1,4 @@ -name: Maven Deploy Artifact +name: Deploy Docker Image on: push: @@ -7,13 +7,9 @@ on: - 'main' - 'epic/**' -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - jobs: - deploy: - uses: valitydev/java-workflow/.github/workflows/maven-service-deploy.yml@v1 + build-and-deploy: + uses: valitydev/base-workflow/.github/workflows/maven-service-deploy.yml@v2-beta secrets: github-token: ${{ secrets.GITHUB_TOKEN }} mm-webhook-url: ${{ secrets.MATTERMOST_WEBHOOK_URL }} diff --git a/pom.xml b/pom.xml index 5ec32881..c4ab43a2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ dev.vality service-parent-pom - 1.0.18 + 2.0.0-BETA-11 newway @@ -177,6 +177,11 @@ limiter-proto 1.32-6158184 + + dev.vality + exrates-proto + 1.3-875328b + dev.vality shared-resources @@ -202,6 +207,12 @@ 1.4.1 test + + org.awaitility + awaitility + 4.2.0 + test + diff --git a/src/main/java/dev/vality/newway/config/KafkaConfig.java b/src/main/java/dev/vality/newway/config/KafkaConfig.java index 9f602da0..df10d067 100644 --- a/src/main/java/dev/vality/newway/config/KafkaConfig.java +++ b/src/main/java/dev/vality/newway/config/KafkaConfig.java @@ -1,8 +1,10 @@ package dev.vality.newway.config; +import dev.vality.exrates.events.CurrencyEvent; import dev.vality.kafka.common.util.ExponentialBackOffDefaultErrorHandlerFactory; import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.config.properties.KafkaConsumerProperties; +import dev.vality.newway.serde.CurrencyExchangeRateEventDeserializer; import dev.vality.newway.serde.PayoutEventDeserializer; import dev.vality.newway.serde.SinkEventDeserializer; import dev.vality.payout.manager.Event; @@ -33,6 +35,9 @@ public class KafkaConfig { @Value("${kafka.topics.party-management.consumer.group-id}") private String partyConsumerGroup; + @Value("${kafka.topics.exrate.consumer.group-id}") + private String exrateConsumerGroup; + @Bean public Map consumerConfigs() { return createConsumerConfig(); @@ -136,9 +141,20 @@ public KafkaListenerContainerFactory> createConcurrentFactory( - ConsumerFactory consumerFactory, int threadsNumber) { - ConcurrentKafkaListenerContainerFactory factory = + @Bean + public KafkaListenerContainerFactory> exchangeRateContainerFactory() { + Map props = kafkaProperties.buildConsumerProperties(); + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, CurrencyExchangeRateEventDeserializer.class); + props.put(ConsumerConfig.GROUP_ID_CONFIG, exrateConsumerGroup); + ConsumerFactory consumerFactory = new DefaultKafkaConsumerFactory<>(props); + + return createConcurrentFactory(consumerFactory, kafkaConsumerProperties.getExrateConcurrency()); + } + + private KafkaListenerContainerFactory> createConcurrentFactory( + ConsumerFactory consumerFactory, int threadsNumber) { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); initFactory(consumerFactory, threadsNumber, factory); return factory; diff --git a/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java b/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java index a4dab743..b6536a6f 100644 --- a/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java +++ b/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java @@ -25,5 +25,6 @@ public class KafkaConsumerProperties { private int destinationConcurrency; private int withdrawalSessionConcurrency; private int limitConfigConcurrency; + private int exrateConcurrency; } diff --git a/src/main/java/dev/vality/newway/dao/exrate/iface/ExchangeRateDao.java b/src/main/java/dev/vality/newway/dao/exrate/iface/ExchangeRateDao.java new file mode 100644 index 00000000..19bda164 --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/exrate/iface/ExchangeRateDao.java @@ -0,0 +1,12 @@ +package dev.vality.newway.dao.exrate.iface; + +import dev.vality.dao.DaoException; +import dev.vality.dao.GenericDao; +import dev.vality.newway.domain.tables.pojos.ExRate; + +import java.util.List; + +public interface ExchangeRateDao extends GenericDao { + void saveBatch(List exchangeRates) throws DaoException; + ExRate findBySourceSymbolicCode(String symbolicCode); +} diff --git a/src/main/java/dev/vality/newway/dao/exrate/impl/ExchangeRateDaoImpl.java b/src/main/java/dev/vality/newway/dao/exrate/impl/ExchangeRateDaoImpl.java new file mode 100644 index 00000000..22474563 --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/exrate/impl/ExchangeRateDaoImpl.java @@ -0,0 +1,48 @@ +package dev.vality.newway.dao.exrate.impl; + +import dev.vality.dao.DaoException; +import dev.vality.dao.impl.AbstractGenericDao; +import dev.vality.mapper.RecordRowMapper; +import dev.vality.newway.dao.exrate.iface.ExchangeRateDao; +import dev.vality.newway.domain.tables.pojos.ExRate; +import org.jooq.Query; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.List; +import java.util.stream.Collectors; + +import static dev.vality.newway.domain.tables.ExRate.EX_RATE; + +@Component +public class ExchangeRateDaoImpl extends AbstractGenericDao implements ExchangeRateDao { + + private final RowMapper rowMapper; + + @Autowired + public ExchangeRateDaoImpl(@Qualifier("dataSource") DataSource dataSource) { + super(dataSource); + this.rowMapper = new RecordRowMapper<>(EX_RATE, ExRate.class); + } + + @Override + public void saveBatch(List exchangeRates) throws DaoException { + List queryList = exchangeRates.stream() + .map(exrate -> getDslContext().newRecord(EX_RATE, exrate)) + .map(record -> (Query) getDslContext().insertInto(EX_RATE).set(record) + .onConflict(EX_RATE.EVENT_ID) + .doNothing()) + .collect(Collectors.toList()); + batchExecute(queryList); + } + + @Override + public ExRate findBySourceSymbolicCode(String symbolicCode) { + Query query = getDslContext().selectFrom(EX_RATE) + .where(EX_RATE.SOURCE_CURRENCY_SYMBOLIC_CODE.eq(symbolicCode)); + return fetchOne(query, rowMapper); + } +} diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/exrate/CurrencyExchangeRateHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/exrate/CurrencyExchangeRateHandler.java new file mode 100644 index 00000000..3adec16d --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/exrate/CurrencyExchangeRateHandler.java @@ -0,0 +1,47 @@ +package dev.vality.newway.handler.event.stock.impl.exrate; + +import dev.vality.exrates.events.CurrencyEvent; +import dev.vality.exrates.events.CurrencyExchangeRate; +import dev.vality.geck.common.util.TypeUtil; +import dev.vality.newway.dao.exrate.iface.ExchangeRateDao; +import dev.vality.newway.domain.tables.pojos.ExRate; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +@Slf4j +@Component +@RequiredArgsConstructor +public class CurrencyExchangeRateHandler implements ExchangeRateHandler { + + private final ExchangeRateDao exchangeRateDao; + + @Override + public void handle(List events) { + List exrates = events.stream().map(currencyEvent -> { + CurrencyExchangeRate exchangeRate = currencyEvent.getPayload().getExchangeRate(); + ExRate exrate = new ExRate(); + exrate.setEventId(UUID.fromString(currencyEvent.getEventId())); + exrate.setEventCreatedAt(TypeUtil.stringToLocalDateTime(currencyEvent.getEventCreatedAt())); + exrate.setSourceCurrencySymbolicCode(exchangeRate.getSourceCurrency().getSymbolicCode()); + exrate.setSourceCurrencyExponent(exchangeRate.getSourceCurrency().getExponent()); + exrate.setDestinationCurrencySymbolicCode(exchangeRate.getDestinationCurrency().getSymbolicCode()); + exrate.setDestinationCurrencyExponent(exchangeRate.getDestinationCurrency().getExponent()); + exrate.setRationalP(exchangeRate.getExchangeRate().getP()); + exrate.setRationalQ(exchangeRate.getExchangeRate().getQ()); + exrate.setRateTimestamp(TypeUtil.stringToLocalDateTime(exchangeRate.timestamp)); + return exrate; + }).collect(Collectors.toList()); + + exchangeRateDao.saveBatch(exrates); + } + + @Override + public boolean isHandle(CurrencyEvent currencyEvent) { + return currencyEvent.payload.isSetExchangeRate(); + } +} diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/exrate/ExchangeRateHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/exrate/ExchangeRateHandler.java new file mode 100644 index 00000000..006df3ae --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/exrate/ExchangeRateHandler.java @@ -0,0 +1,12 @@ +package dev.vality.newway.handler.event.stock.impl.exrate; + +import dev.vality.exrates.events.CurrencyEvent; + +import java.util.List; + +public interface ExchangeRateHandler { + + void handle(List currencyEvents); + + boolean isHandle(CurrencyEvent currencyEvent); +} diff --git a/src/main/java/dev/vality/newway/listener/ExchangeRateListener.java b/src/main/java/dev/vality/newway/listener/ExchangeRateListener.java new file mode 100644 index 00000000..7fb72020 --- /dev/null +++ b/src/main/java/dev/vality/newway/listener/ExchangeRateListener.java @@ -0,0 +1,36 @@ +package dev.vality.newway.listener; + +import dev.vality.exrates.events.CurrencyEvent; +import dev.vality.newway.service.ExchangeRateService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.support.Acknowledgment; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@RequiredArgsConstructor +@Service +public class ExchangeRateListener { + + private final ExchangeRateService exchangeRateService; + + @KafkaListener( + autoStartup = "${kafka.topics.exrate.enabled}", + topics = "${kafka.topics.exrate.id}", + containerFactory = "exchangeRateContainerFactory") + public void handle(List> messages, Acknowledgment ack) { + log.info("Got ExchangeRate messages batch with size: {}", messages.size()); + exchangeRateService.handleEvents( + messages.stream() + .map(ConsumerRecord::value) + .collect(Collectors.toList()) + ); + ack.acknowledge(); + log.info("Batch ExchangeRate has been committed, size={}", messages.size()); + } +} diff --git a/src/main/java/dev/vality/newway/serde/CurrencyExchangeRateEventDeserializer.java b/src/main/java/dev/vality/newway/serde/CurrencyExchangeRateEventDeserializer.java new file mode 100644 index 00000000..53096d9b --- /dev/null +++ b/src/main/java/dev/vality/newway/serde/CurrencyExchangeRateEventDeserializer.java @@ -0,0 +1,11 @@ +package dev.vality.newway.serde; + +import dev.vality.exrates.events.CurrencyEvent; +import dev.vality.kafka.common.serialization.AbstractThriftDeserializer; + +public class CurrencyExchangeRateEventDeserializer extends AbstractThriftDeserializer { + @Override + public CurrencyEvent deserialize(String topic, byte[] data) { + return deserialize(data, new CurrencyEvent()); + } +} diff --git a/src/main/java/dev/vality/newway/service/ExchangeRateService.java b/src/main/java/dev/vality/newway/service/ExchangeRateService.java new file mode 100644 index 00000000..1f9153ab --- /dev/null +++ b/src/main/java/dev/vality/newway/service/ExchangeRateService.java @@ -0,0 +1,29 @@ +package dev.vality.newway.service; + +import dev.vality.exrates.events.CurrencyEvent; +import dev.vality.newway.handler.event.stock.impl.exrate.ExchangeRateHandler; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class ExchangeRateService { + + private final List exchangeRateHandlers; + + @Transactional(propagation = Propagation.REQUIRED) + public void handleEvents(List events) { + events.stream() + .collect(Collectors.groupingBy( + currencyEvent -> exchangeRateHandlers.stream() + .filter(exchangeRateHandler -> exchangeRateHandler.isHandle(currencyEvent)) + .findAny().orElseThrow()) + ).forEach(ExchangeRateHandler::handle); + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1ac43272..545201fc 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -67,6 +67,7 @@ kafka: destination-concurrency: 7 withdrawal-session-concurrency: 7 limit-config-concurrency: 7 + exrate-concurrency: 7 topics: invoice: id: mg-invoice-100-2 @@ -108,6 +109,10 @@ kafka: limit-config: id: mg-events-lim-config enabled: false + exrate: + id: etl-exchange-rate + enabled: false + consumer.group-id: "daway-exrate" dmt: url: http://dominant:8022/v1/domain/repository diff --git a/src/main/resources/db/migration/V7__add_new_exchange_rates.sql b/src/main/resources/db/migration/V7__add_new_exchange_rates.sql new file mode 100644 index 00000000..4a06012c --- /dev/null +++ b/src/main/resources/db/migration/V7__add_new_exchange_rates.sql @@ -0,0 +1,19 @@ +CREATE TABLE dw.ex_rate +( + id BIGSERIAL NOT NULL, + event_id uuid UNIQUE NOT NULL, + event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + source_currency_symbolic_code CHARACTER VARYING NOT NULL, + source_currency_exponent SMALLINT NOT NULL, + destination_currency_symbolic_code CHARACTER VARYING NOT NULL, + destination_currency_exponent SMALLINT NOT NULL, + rational_p BIGINT NOT NULL, + rational_q BIGINT NOT NULL, + rate_timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL +); + +CREATE INDEX rate_timestamp_idx ON dw.ex_rate (rate_timestamp); + +CREATE INDEX source_currency_sc_destination_currency_sc_timestamp_idx ON dw.ex_rate (source_currency_symbolic_code, + destination_currency_symbolic_code, + rate_timestamp); diff --git a/src/test/java/dev/vality/newway/config/KafkaPostgresqlSpringBootITest.java b/src/test/java/dev/vality/newway/config/KafkaPostgresqlSpringBootITest.java index 21722a42..88f9bf01 100644 --- a/src/test/java/dev/vality/newway/config/KafkaPostgresqlSpringBootITest.java +++ b/src/test/java/dev/vality/newway/config/KafkaPostgresqlSpringBootITest.java @@ -28,7 +28,8 @@ "kafka.topics.source.enabled=true", "kafka.topics.destination.enabled=true", "kafka.topics.pm-events-payout.enabled=true", - "kafka.topics.limit-config.enabled=true"}, + "kafka.topics.limit-config.enabled=true", + "kafka.topics.exrate.enabled=true"}, topicsKeys = { "kafka.topics.invoice.id", "kafka.topics.recurrent-payment-tool.id", @@ -42,7 +43,10 @@ "kafka.topics.source.id", "kafka.topics.destination.id", "kafka.topics.pm-events-payout.id", - "kafka.topics.limit-config.id"}) + "kafka.topics.limit-config.id", + "kafka.topics.limit-config.id", + "kafka.topics.exrate.id"} +) @DefaultSpringBootTest @Import(KafkaProducer.class) public @interface KafkaPostgresqlSpringBootITest { diff --git a/src/test/java/dev/vality/newway/kafka/ExchangeRateKafkaListenerTest.java b/src/test/java/dev/vality/newway/kafka/ExchangeRateKafkaListenerTest.java new file mode 100644 index 00000000..e1db09f9 --- /dev/null +++ b/src/test/java/dev/vality/newway/kafka/ExchangeRateKafkaListenerTest.java @@ -0,0 +1,89 @@ +package dev.vality.newway.kafka; + +import dev.vality.exrates.base.Rational; +import dev.vality.exrates.events.Currency; +import dev.vality.exrates.events.CurrencyEvent; +import dev.vality.exrates.events.CurrencyEventPayload; +import dev.vality.exrates.events.CurrencyExchangeRate; +import dev.vality.geck.common.util.TypeUtil; +import dev.vality.newway.config.KafkaPostgresqlSpringBootITest; +import dev.vality.newway.dao.exrate.iface.ExchangeRateDao; +import dev.vality.newway.domain.tables.pojos.ExRate; +import dev.vality.newway.service.ExchangeRateService; +import org.apache.thrift.TBase; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.mock.mockito.SpyBean; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; + +@KafkaPostgresqlSpringBootITest +public class ExchangeRateKafkaListenerTest { + + @Value("${kafka.topics.exrate.id}") + public String topic; + + @Autowired + private dev.vality.testcontainers.annotations.kafka.config.KafkaProducer> testThriftKafkaProducer; + + @SpyBean + private ExchangeRateService exchangeRateService; + + @Autowired + private ExchangeRateDao exchangeRateDao; + + @Test + public void listenExchangeRateEventTest() { + // Given + CurrencyEvent currencyEvent = buildCurrencyEvent(); + CurrencyExchangeRate exchangeRate = currencyEvent.payload.getExchangeRate(); + + // When + testThriftKafkaProducer.send(topic, currencyEvent); + await().atMost(30, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).until(() -> { + return exchangeRateDao.findBySourceSymbolicCode(exchangeRate.getSourceCurrency().getSymbolicCode()) != null; + }); + ExRate exrate = exchangeRateDao.findBySourceSymbolicCode(exchangeRate.getSourceCurrency().getSymbolicCode()); + + // Then + verify(exchangeRateService, timeout(TimeUnit.MINUTES.toMillis(1)).times(1)).handleEvents(anyList()); + assertEquals(exchangeRate.getSourceCurrency().getSymbolicCode(), exrate.getSourceCurrencySymbolicCode()); + assertEquals(exchangeRate.getSourceCurrency().getExponent(), exrate.getSourceCurrencyExponent()); + assertEquals(exchangeRate.getDestinationCurrency().getSymbolicCode(), exrate.getDestinationCurrencySymbolicCode()); + assertEquals(exchangeRate.getDestinationCurrency().getExponent(), exrate.getDestinationCurrencyExponent()); + assertEquals(exchangeRate.getExchangeRate().p, exrate.getRationalP()); + assertEquals(exchangeRate.getExchangeRate().q, exrate.getRationalQ()); + assertEquals(TypeUtil.stringToLocalDateTime(exchangeRate.getTimestamp()).truncatedTo(ChronoUnit.SECONDS), + exrate.getRateTimestamp().truncatedTo(ChronoUnit.SECONDS)); + } + + private CurrencyEvent buildCurrencyEvent() { + CurrencyEvent currencyEvent = new CurrencyEvent(); + currencyEvent.setEventId(UUID.randomUUID().toString()); + currencyEvent.setEventCreatedAt(TypeUtil.temporalToString(LocalDateTime.now())); + currencyEvent.setPayload( + CurrencyEventPayload.exchange_rate( + new CurrencyExchangeRate( + new Currency("USD", (short) 2), + new Currency("RUB", (short) 2), + new Rational(60797502, 1000000), + TypeUtil.temporalToString(Instant.now()) + ) + ) + ); + + return currencyEvent; + } + +} From 5ee616dac782c33340572da9069f2d537f7031af Mon Sep 17 00:00:00 2001 From: Anatoly Karlov Date: Thu, 24 Nov 2022 15:35:30 +0600 Subject: [PATCH 15/36] add new limiter scopes (#73) --- pom.xml | 2 +- src/main/resources/db/migration/V8__add_new_limiter_scopes.sql | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/db/migration/V8__add_new_limiter_scopes.sql diff --git a/pom.xml b/pom.xml index c4ab43a2..bfed5994 100644 --- a/pom.xml +++ b/pom.xml @@ -175,7 +175,7 @@ dev.vality limiter-proto - 1.32-6158184 + 1.33-31de59b dev.vality diff --git a/src/main/resources/db/migration/V8__add_new_limiter_scopes.sql b/src/main/resources/db/migration/V8__add_new_limiter_scopes.sql new file mode 100644 index 00000000..60524f5e --- /dev/null +++ b/src/main/resources/db/migration/V8__add_new_limiter_scopes.sql @@ -0,0 +1,3 @@ +ALTER TYPE dw.limit_config_limit_scope_type ADD VALUE 'provider'; +ALTER TYPE dw.limit_config_limit_scope_type ADD VALUE 'terminal'; +ALTER TYPE dw.limit_config_limit_scope_type ADD VALUE 'payer_contact_email'; From c82b4aad68f2d1ef178b9cf21f28f44f06cb30e4 Mon Sep 17 00:00:00 2001 From: malkoas Date: Mon, 12 Dec 2022 11:47:06 +0300 Subject: [PATCH 16/36] Add exception handling in DominantPoller --- .../java/dev/vality/newway/handler/dominant/DominantPoller.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java index 7e4ded82..faf11e89 100644 --- a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java +++ b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java @@ -35,7 +35,7 @@ public void process() { pullRange.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .forEach(e -> handleDominantData(after, versionId, e)); - } catch (TException e) { + } catch (Exception e) { log.warn("Error to polling dominant, after={}", after, e); } } From d9ca88eed6839b574ef18bb1bee96057cc952aed Mon Sep 17 00:00:00 2001 From: malkoas Date: Mon, 12 Dec 2022 11:59:34 +0300 Subject: [PATCH 17/36] Revert "Add exception handling in DominantPoller" This reverts commit c82b4aad68f2d1ef178b9cf21f28f44f06cb30e4. --- .../java/dev/vality/newway/handler/dominant/DominantPoller.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java index faf11e89..7e4ded82 100644 --- a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java +++ b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java @@ -35,7 +35,7 @@ public void process() { pullRange.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .forEach(e -> handleDominantData(after, versionId, e)); - } catch (Exception e) { + } catch (TException e) { log.warn("Error to polling dominant, after={}", after, e); } } From a95f51329177a8cef3a802207a674f185a3b723d Mon Sep 17 00:00:00 2001 From: malkoas Date: Mon, 12 Dec 2022 12:05:23 +0300 Subject: [PATCH 18/36] Add exception handling in DominantPoller --- .../java/dev/vality/newway/handler/dominant/DominantPoller.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java index 7e4ded82..faf11e89 100644 --- a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java +++ b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java @@ -35,7 +35,7 @@ public void process() { pullRange.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .forEach(e -> handleDominantData(after, versionId, e)); - } catch (TException e) { + } catch (Exception e) { log.warn("Error to polling dominant, after={}", after, e); } } From f02e8271f2392ac64f51b65f6f470c50306d61ed Mon Sep 17 00:00:00 2001 From: malkoas Date: Mon, 12 Dec 2022 12:06:31 +0300 Subject: [PATCH 19/36] Revert "Add exception handling in DominantPoller" This reverts commit a95f51329177a8cef3a802207a674f185a3b723d. --- .../java/dev/vality/newway/handler/dominant/DominantPoller.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java index faf11e89..7e4ded82 100644 --- a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java +++ b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java @@ -35,7 +35,7 @@ public void process() { pullRange.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .forEach(e -> handleDominantData(after, versionId, e)); - } catch (Exception e) { + } catch (TException e) { log.warn("Error to polling dominant, after={}", after, e); } } From 72f4c1764bdfa5cc576a1d1dd89a061c50a79d2b Mon Sep 17 00:00:00 2001 From: malkoas Date: Mon, 12 Dec 2022 12:07:17 +0300 Subject: [PATCH 20/36] Add exception handling in DominantPoller --- .../java/dev/vality/newway/handler/dominant/DominantPoller.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java index 7e4ded82..faf11e89 100644 --- a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java +++ b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java @@ -35,7 +35,7 @@ public void process() { pullRange.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .forEach(e -> handleDominantData(after, versionId, e)); - } catch (TException e) { + } catch (Exception e) { log.warn("Error to polling dominant, after={}", after, e); } } From 65eaa71e3d4325f6500717054b24067b04cd15d2 Mon Sep 17 00:00:00 2001 From: malkoas Date: Mon, 12 Dec 2022 12:09:30 +0300 Subject: [PATCH 21/36] Revert "Add exception handling in DominantPoller" This reverts commit 72f4c1764bdfa5cc576a1d1dd89a061c50a79d2b. --- .../java/dev/vality/newway/handler/dominant/DominantPoller.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java index faf11e89..7e4ded82 100644 --- a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java +++ b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java @@ -35,7 +35,7 @@ public void process() { pullRange.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .forEach(e -> handleDominantData(after, versionId, e)); - } catch (Exception e) { + } catch (TException e) { log.warn("Error to polling dominant, after={}", after, e); } } From bf3311a0ba2d71fc1d4934624377e7d5c475ea87 Mon Sep 17 00:00:00 2001 From: malkoas <41993717+malkoas@users.noreply.github.com> Date: Mon, 12 Dec 2022 12:35:13 +0300 Subject: [PATCH 22/36] Add exception handling in DominantPoller (#76) --- .../dev/vality/newway/handler/dominant/DominantPoller.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java index 7e4ded82..cc0b8c3b 100644 --- a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java +++ b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java @@ -6,7 +6,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; -import org.apache.thrift.TException; import org.springframework.context.annotation.DependsOn; import org.springframework.scheduling.annotation.Scheduled; @@ -35,7 +34,7 @@ public void process() { pullRange.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .forEach(e -> handleDominantData(after, versionId, e)); - } catch (TException e) { + } catch (Exception e) { log.warn("Error to polling dominant, after={}", after, e); } } From 2703a2ecf5c995abfae1e6e751106b063d8207c9 Mon Sep 17 00:00:00 2001 From: struga Date: Mon, 20 Feb 2023 17:01:10 +0700 Subject: [PATCH 23/36] Fix polling (#88) --- .../dao/dominant/iface/DominantDao.java | 1 + .../dao/dominant/impl/DominantDaoImpl.java | 34 ++++------- .../handler/dominant/DominantPoller.java | 1 + .../newway/service/DominantService.java | 5 ++ .../V9__dominant_last_version_id.sql | 58 +++++++++++++++++++ .../java/dev/vality/newway/dao/DaoTests.java | 5 +- 6 files changed, 78 insertions(+), 26 deletions(-) create mode 100644 src/main/resources/db/migration/V9__dominant_last_version_id.sql diff --git a/src/main/java/dev/vality/newway/dao/dominant/iface/DominantDao.java b/src/main/java/dev/vality/newway/dao/dominant/iface/DominantDao.java index 420e8605..3c493eca 100644 --- a/src/main/java/dev/vality/newway/dao/dominant/iface/DominantDao.java +++ b/src/main/java/dev/vality/newway/dao/dominant/iface/DominantDao.java @@ -5,4 +5,5 @@ public interface DominantDao extends GenericDao { Long getLastVersionId() throws DaoException; + void updateLastVersionId(Long versionId) throws DaoException; } diff --git a/src/main/java/dev/vality/newway/dao/dominant/impl/DominantDaoImpl.java b/src/main/java/dev/vality/newway/dao/dominant/impl/DominantDaoImpl.java index 773dd2b5..e7263a69 100644 --- a/src/main/java/dev/vality/newway/dao/dominant/impl/DominantDaoImpl.java +++ b/src/main/java/dev/vality/newway/dao/dominant/impl/DominantDaoImpl.java @@ -21,30 +21,16 @@ public DominantDaoImpl(DataSource dataSource) { @Override public Long getLastVersionId() throws DaoException { - Query query = getDslContext().select(max(DSL.field("max"))).from( - getDslContext().select(max(Tables.CALENDAR.VERSION_ID)).from(Tables.CALENDAR) - .unionAll(getDslContext().select(max(Tables.CATEGORY.VERSION_ID)).from(Tables.CATEGORY)) - .unionAll(getDslContext().select(max(Tables.COUNTRY.VERSION_ID)).from(Tables.COUNTRY)) - .unionAll(getDslContext().select(max(Tables.CURRENCY.VERSION_ID)).from(Tables.CURRENCY)) - .unionAll(getDslContext().select(max(Tables.INSPECTOR.VERSION_ID)).from(Tables.INSPECTOR)) - .unionAll(getDslContext().select(max(Tables.PAYMENT_INSTITUTION.VERSION_ID)) - .from(Tables.PAYMENT_INSTITUTION)) - .unionAll(getDslContext().select(max(Tables.PAYMENT_METHOD.VERSION_ID)) - .from(Tables.PAYMENT_METHOD)) - .unionAll(getDslContext().select(max(Tables.PAYOUT_METHOD.VERSION_ID)) - .from(Tables.PAYOUT_METHOD)) - .unionAll(getDslContext().select(max(Tables.PROVIDER.VERSION_ID)).from(Tables.PROVIDER)) - .unionAll(getDslContext().select(max(Tables.PROXY.VERSION_ID)).from(Tables.PROXY)) - .unionAll(getDslContext().select(max(Tables.TERMINAL.VERSION_ID)).from(Tables.TERMINAL)) - .unionAll(getDslContext().select(max(Tables.TERM_SET_HIERARCHY.VERSION_ID)) - .from(Tables.TERM_SET_HIERARCHY)) - .unionAll(getDslContext().select(max(Tables.TRADE_BLOC.VERSION_ID)) - .from(Tables.TRADE_BLOC)) - .unionAll(getDslContext().select(max(Tables.WITHDRAWAL_PROVIDER.VERSION_ID)) - .from(Tables.WITHDRAWAL_PROVIDER)) - .unionAll(getDslContext().select(max(Tables.PAYMENT_ROUTING_RULE.VERSION_ID)) - .from(Tables.PAYMENT_ROUTING_RULE)) - ); + Query query = getDslContext() + .select(Tables.DOMINANT_LAST_VERSION_ID.VERSION_ID) + .from(Tables.DOMINANT_LAST_VERSION_ID); return fetchOne(query, Long.class); } + + @Override + public void updateLastVersionId(Long versionId) throws DaoException { + Query query = getDslContext().update(Tables.DOMINANT_LAST_VERSION_ID) + .set(Tables.DOMINANT_LAST_VERSION_ID.VERSION_ID, versionId); + executeOne(query); + } } diff --git a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java index cc0b8c3b..c779854f 100644 --- a/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java +++ b/src/main/java/dev/vality/newway/handler/dominant/DominantPoller.java @@ -44,6 +44,7 @@ private void handleDominantData(AtomicLong after, AtomicLong versionId, Map.Entr try { versionId.set(e.getKey()); dominantService.processCommit(versionId.get(), e); + dominantService.updateLastVersionId(versionId.get()); after.set(versionId.get()); } catch (RuntimeException ex) { throw new RuntimeException( diff --git a/src/main/java/dev/vality/newway/service/DominantService.java b/src/main/java/dev/vality/newway/service/DominantService.java index 33906952..4390821a 100644 --- a/src/main/java/dev/vality/newway/service/DominantService.java +++ b/src/main/java/dev/vality/newway/service/DominantService.java @@ -51,4 +51,9 @@ public Optional getLastVersionId() { log.info("Last dominant versionId={}", lastVersionId); return lastVersionId; } + + public void updateLastVersionId(Long lastVersionId) { + dominantDao.updateLastVersionId(lastVersionId); + log.info("Last dominant versionId={} is updated", lastVersionId); + } } diff --git a/src/main/resources/db/migration/V9__dominant_last_version_id.sql b/src/main/resources/db/migration/V9__dominant_last_version_id.sql new file mode 100644 index 00000000..5fb24eaa --- /dev/null +++ b/src/main/resources/db/migration/V9__dominant_last_version_id.sql @@ -0,0 +1,58 @@ +CREATE TABLE dw.dominant_last_version_id +( + version_id BIGINT NOT NULL, + wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() at time zone 'utc') +); + +insert into dw.dominant_last_version_id(version_id) +values ((with max_ids as ( + select max(version_id) as id + from dw.CALENDAR + union all + select max(version_id) as id + from dw.CATEGORY + union all + select max(version_id) as id + from dw.COUNTRY + union all + select max(version_id) as id + from dw.CURRENCY + union all + select max(version_id) as id + from dw.INSPECTOR + union all + select max(version_id) as id + from dw.PAYMENT_INSTITUTION + union all + select max(version_id) as id + from dw.PAYMENT_METHOD + union all + select max(version_id) as id + from dw.PAYOUT_METHOD + union all + select max(version_id) as id + from dw.PROVIDER + union all + select max(version_id) as id + from dw.PROXY + union all + select max(version_id) as id + from dw.TERMINAL + union all + select max(version_id) as id + from dw.TERM_SET_HIERARCHY + union all + select max(version_id) as id + from dw.TRADE_BLOC + union all + select max(version_id) as id + from dw.WITHDRAWAL_PROVIDER + union all + select max(version_id) as id + from dw.PAYMENT_ROUTING_RULE + union all + select max(version_id) as id + from dw.CATEGORY) + select coalesce(max(id), 0) + from max_ids)) +; \ No newline at end of file diff --git a/src/test/java/dev/vality/newway/dao/DaoTests.java b/src/test/java/dev/vality/newway/dao/DaoTests.java index c5e4a068..0b187f36 100644 --- a/src/test/java/dev/vality/newway/dao/DaoTests.java +++ b/src/test/java/dev/vality/newway/dao/DaoTests.java @@ -189,8 +189,6 @@ public void dominantDaoTest() { termSetHierarchyDao.save(termSetHierarchy); termSetHierarchyDao.updateNotCurrent(termSetHierarchy.getTermSetHierarchyRefId()); - Long lastVersionId = dominantDao.getLastVersionId(); - OptionalLong maxVersionId = LongStream.of( calendar.getVersionId(), category.getVersionId(), @@ -205,6 +203,9 @@ public void dominantDaoTest() { terminal.getVersionId(), termSetHierarchy.getVersionId()).max(); + dominantDao.updateLastVersionId(maxVersionId.getAsLong()); + Long lastVersionId = dominantDao.getLastVersionId(); + assertEquals(maxVersionId.getAsLong(), lastVersionId.longValue()); } From 67895fec0dbc3682c091527047589dd0b32098b9 Mon Sep 17 00:00:00 2001 From: struga Date: Mon, 20 Feb 2023 17:22:32 +0700 Subject: [PATCH 24/36] Fix build (#89) --- .github/workflows/build.yml | 2 +- .github/workflows/deploy.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 38080b60..e5cf3aa5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,4 +7,4 @@ on: jobs: build: - uses: valitydev/base-workflow/.github/workflows/maven-service-build.yml@v2-beta + uses: valitydev/base-workflow/.github/workflows/maven-service-build.yml@v2 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d464b920..72a7a272 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,7 +9,7 @@ on: jobs: build-and-deploy: - uses: valitydev/base-workflow/.github/workflows/maven-service-deploy.yml@v2-beta + uses: valitydev/base-workflow/.github/workflows/maven-service-deploy.yml@v2 secrets: github-token: ${{ secrets.GITHUB_TOKEN }} mm-webhook-url: ${{ secrets.MATTERMOST_WEBHOOK_URL }} From f3c1f8cd69a799ef7a7a25f04035ef47a6469fe9 Mon Sep 17 00:00:00 2001 From: Pavel Popov Date: Fri, 19 May 2023 16:18:03 +0700 Subject: [PATCH 25/36] Bump damsel --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bfed5994..2099b934 100644 --- a/pom.xml +++ b/pom.xml @@ -160,7 +160,7 @@ dev.vality damsel - 1.569-09e7a75 + 1.596-52dbefd dev.vality From 72f6b9f304e3559d3b45b4b69ddf6ad81baeda14 Mon Sep 17 00:00:00 2001 From: struga Date: Thu, 15 Jun 2023 09:54:15 +0300 Subject: [PATCH 26/36] Add session info (#92) --- .../iface/PaymentSessionInfoDao.java | 15 +++++ .../impl/PaymentSessionInfoDaoImpl.java | 63 +++++++++++++++++ .../payment/PaymentSessionWrapperHandler.java | 35 ++++++++++ ...cePaymentSessionChangeActivatedMapper.java | 58 ++++++++++++++++ ...icePaymentSessionChangeFinishedMapper.java | 67 +++++++++++++++++++ ...oicePaymentSessionChangeStartedMapper.java | 66 ++++++++++++++++++ ...cePaymentSessionChangeSuspendedMapper.java | 66 ++++++++++++++++++ .../vality/newway/model/PaymentWrapper.java | 1 + .../db/migration/V10__add_session_status.sql | 22 ++++++ .../java/dev/vality/newway/dao/DaoTests.java | 2 + .../service/PaymentWrapperServiceTest.java | 5 ++ .../newway/utils/PaymentWrapperTestUtil.java | 4 ++ 12 files changed, 404 insertions(+) create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentSessionInfoDao.java create mode 100644 src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentSessionInfoDaoImpl.java create mode 100644 src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentSessionWrapperHandler.java create mode 100644 src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeActivatedMapper.java create mode 100644 src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeFinishedMapper.java create mode 100644 src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeStartedMapper.java create mode 100644 src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeSuspendedMapper.java create mode 100644 src/main/resources/db/migration/V10__add_session_status.sql diff --git a/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentSessionInfoDao.java b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentSessionInfoDao.java new file mode 100644 index 00000000..057b8f95 --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/iface/PaymentSessionInfoDao.java @@ -0,0 +1,15 @@ +package dev.vality.newway.dao.invoicing.iface; + +import dev.vality.dao.GenericDao; +import dev.vality.newway.domain.tables.pojos.PaymentRoute; +import dev.vality.newway.domain.tables.pojos.PaymentSessionInfo; +import dev.vality.newway.exception.DaoException; + +import java.util.List; + +public interface PaymentSessionInfoDao extends GenericDao { + + void saveBatch(List paymentStatusInfos) throws DaoException; + + PaymentSessionInfo get(String invoiceId, String paymentId) throws DaoException; +} \ No newline at end of file diff --git a/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentSessionInfoDaoImpl.java b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentSessionInfoDaoImpl.java new file mode 100644 index 00000000..b36f7372 --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/invoicing/impl/PaymentSessionInfoDaoImpl.java @@ -0,0 +1,63 @@ +package dev.vality.newway.dao.invoicing.impl; + +import dev.vality.dao.impl.AbstractGenericDao; +import dev.vality.mapper.RecordRowMapper; +import dev.vality.newway.dao.invoicing.iface.PaymentSessionInfoDao; +import dev.vality.newway.domain.tables.pojos.PaymentSessionInfo; +import dev.vality.newway.domain.tables.records.PaymentSessionInfoRecord; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.exception.NotFoundException; +import org.jooq.Query; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static dev.vality.newway.domain.tables.PaymentSessionInfo.PAYMENT_SESSION_INFO; + +@Component +public class PaymentSessionInfoDaoImpl extends AbstractGenericDao implements PaymentSessionInfoDao { + + private final RowMapper rowMapper; + + public PaymentSessionInfoDaoImpl(DataSource dataSource) { + super(dataSource); + this.rowMapper = new RecordRowMapper<>(PAYMENT_SESSION_INFO, PaymentSessionInfo.class); + } + + @Override + public void saveBatch(List paymentSessionInfos) throws DaoException { + List queries = paymentSessionInfos.stream() + .map(statusInfo -> getDslContext().newRecord(PAYMENT_SESSION_INFO, statusInfo)) + .map(this::prepareInsertQuery) + .collect(Collectors.toList()); + batchExecute(queries); + } + + @Override + public PaymentSessionInfo get(String invoiceId, String paymentId) throws DaoException { + Query query = getDslContext().selectFrom(PAYMENT_SESSION_INFO) + .where(PAYMENT_SESSION_INFO.INVOICE_ID.eq(invoiceId) + .and(PAYMENT_SESSION_INFO.PAYMENT_ID.eq(paymentId))); + return Optional.ofNullable(fetchOne(query, rowMapper)).orElseThrow(() -> + new NotFoundException( + "PaymentPayerInfo not found, invoiceId=" + invoiceId + " paymentId=" + paymentId)); + } + + + private Query prepareInsertQuery(PaymentSessionInfoRecord record) { + return getDslContext().insertInto(PAYMENT_SESSION_INFO) + .set(record) + .onConflict( + PAYMENT_SESSION_INFO.INVOICE_ID, + PAYMENT_SESSION_INFO.PAYMENT_ID, + PAYMENT_SESSION_INFO.SEQUENCE_ID, + PAYMENT_SESSION_INFO.CHANGE_ID + ) + .doNothing(); + } + +} diff --git a/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentSessionWrapperHandler.java b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentSessionWrapperHandler.java new file mode 100644 index 00000000..4e2e2e09 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/wrapper/payment/PaymentSessionWrapperHandler.java @@ -0,0 +1,35 @@ +package dev.vality.newway.handler.wrapper.payment; + +import dev.vality.newway.dao.invoicing.iface.PaymentSessionInfoDao; +import dev.vality.newway.domain.tables.pojos.PaymentSessionInfo; +import dev.vality.newway.handler.wrapper.WrapperHandler; +import dev.vality.newway.model.PaymentWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Component +public class PaymentSessionWrapperHandler implements WrapperHandler { + + private final PaymentSessionInfoDao paymentSessionInfoDao; + + @Override + public boolean accept(List wrappers) { + return wrappers.stream() + .map(PaymentWrapper::getPaymentSessionInfo) + .anyMatch(Objects::nonNull); + } + + @Override + public void saveBatch(List wrappers) { + List payments = wrappers.stream() + .map(PaymentWrapper::getPaymentSessionInfo) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + paymentSessionInfoDao.saveBatch(payments); + } +} diff --git a/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeActivatedMapper.java b/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeActivatedMapper.java new file mode 100644 index 00000000..0b65ec50 --- /dev/null +++ b/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeActivatedMapper.java @@ -0,0 +1,58 @@ +package dev.vality.newway.mapper.payment.session; + +import dev.vality.damsel.payment_processing.InvoiceChange; +import dev.vality.damsel.payment_processing.InvoicePaymentChange; +import dev.vality.geck.common.util.TypeUtil; +import dev.vality.geck.filter.Filter; +import dev.vality.geck.filter.PathConditionFilter; +import dev.vality.geck.filter.condition.IsNullCondition; +import dev.vality.geck.filter.rule.PathConditionRule; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.domain.enums.PaymentSessionStatus; +import dev.vality.newway.domain.tables.pojos.PaymentSessionInfo; +import dev.vality.newway.mapper.Mapper; +import dev.vality.newway.model.InvoicingKey; +import dev.vality.newway.model.PaymentWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class InvoicePaymentSessionChangeActivatedMapper implements Mapper { + + private Filter filter = new PathConditionFilter(new PathConditionRule( + "invoice_payment_change.payload.invoice_payment_session_change.payload.session_activated", + new IsNullCondition().not())); + + @Override + public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId) { + InvoicePaymentChange invoicePaymentChange = change.getInvoicePaymentChange(); + String invoiceId = event.getSourceId(); + String paymentId = invoicePaymentChange.getId(); + long sequenceId = event.getEventId(); + log.info( + "Start mapping session change activated info, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); + PaymentSessionInfo paymentSessionInfo = new PaymentSessionInfo(); + paymentSessionInfo.setEventCreatedAt(TypeUtil.stringToLocalDateTime(event.getCreatedAt())); + paymentSessionInfo.setInvoiceId(invoiceId); + paymentSessionInfo.setPaymentId(paymentId); + paymentSessionInfo.setSequenceId(sequenceId); + paymentSessionInfo.setChangeId(changeId); + paymentSessionInfo.setSessionStatus(PaymentSessionStatus.activated); + log.info( + "Payment session activated info has been mapped, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); + PaymentWrapper paymentWrapper = new PaymentWrapper(); + paymentWrapper.setKey(InvoicingKey.buildKey(invoiceId, paymentId)); + paymentWrapper.setPaymentSessionInfo(paymentSessionInfo); + return paymentWrapper; + } + + @Override + public Filter getFilter() { + return filter; + } +} diff --git a/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeFinishedMapper.java b/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeFinishedMapper.java new file mode 100644 index 00000000..cb448687 --- /dev/null +++ b/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeFinishedMapper.java @@ -0,0 +1,67 @@ +package dev.vality.newway.mapper.payment.session; + +import dev.vality.damsel.payment_processing.InvoiceChange; +import dev.vality.damsel.payment_processing.InvoicePaymentChange; +import dev.vality.damsel.payment_processing.SessionResult; +import dev.vality.geck.common.util.TypeUtil; +import dev.vality.geck.filter.Filter; +import dev.vality.geck.filter.PathConditionFilter; +import dev.vality.geck.filter.condition.IsNullCondition; +import dev.vality.geck.filter.rule.PathConditionRule; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.domain.enums.PaymentSessionStatus; +import dev.vality.newway.domain.tables.pojos.PaymentSessionInfo; +import dev.vality.newway.mapper.Mapper; +import dev.vality.newway.model.InvoicingKey; +import dev.vality.newway.model.PaymentWrapper; +import dev.vality.newway.util.JsonUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class InvoicePaymentSessionChangeFinishedMapper implements Mapper { + + private Filter filter = new PathConditionFilter(new PathConditionRule( + "invoice_payment_change.payload.invoice_payment_session_change.payload.session_finished", + new IsNullCondition().not())); + + @Override + public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId) { + InvoicePaymentChange invoicePaymentChange = change.getInvoicePaymentChange(); + String invoiceId = event.getSourceId(); + String paymentId = invoicePaymentChange.getId(); + long sequenceId = event.getEventId(); + log.info( + "Start mapping session change finished info, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); + PaymentSessionInfo paymentSessionInfo = new PaymentSessionInfo(); + paymentSessionInfo.setEventCreatedAt(TypeUtil.stringToLocalDateTime(event.getCreatedAt())); + paymentSessionInfo.setInvoiceId(invoiceId); + paymentSessionInfo.setPaymentId(paymentId); + paymentSessionInfo.setSequenceId(sequenceId); + paymentSessionInfo.setChangeId(changeId); + paymentSessionInfo.setSessionStatus(PaymentSessionStatus.finished); + + SessionResult result = + invoicePaymentChange.getPayload().getInvoicePaymentSessionChange().getPayload().getSessionFinished() + .getResult(); + + paymentSessionInfo.setReason(JsonUtil.thriftBaseToJsonString(result.getFailed())); + + log.info( + "Payment session finished has been mapped, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); + PaymentWrapper paymentWrapper = new PaymentWrapper(); + paymentWrapper.setKey(InvoicingKey.buildKey(invoiceId, paymentId)); + paymentWrapper.setPaymentSessionInfo(paymentSessionInfo); + return paymentWrapper; + } + + @Override + public Filter getFilter() { + return filter; + } +} diff --git a/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeStartedMapper.java b/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeStartedMapper.java new file mode 100644 index 00000000..52f609a8 --- /dev/null +++ b/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeStartedMapper.java @@ -0,0 +1,66 @@ +package dev.vality.newway.mapper.payment.session; + +import dev.vality.damsel.domain.AdditionalTransactionInfo; +import dev.vality.damsel.domain.TransactionInfo; +import dev.vality.damsel.payment_processing.InvoiceChange; +import dev.vality.damsel.payment_processing.InvoicePaymentChange; +import dev.vality.damsel.payment_processing.InvoicePaymentSessionChange; +import dev.vality.damsel.payment_processing.SessionChangePayload; +import dev.vality.geck.common.util.TypeUtil; +import dev.vality.geck.filter.Filter; +import dev.vality.geck.filter.PathConditionFilter; +import dev.vality.geck.filter.condition.IsNullCondition; +import dev.vality.geck.filter.rule.PathConditionRule; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.domain.enums.PaymentSessionStatus; +import dev.vality.newway.domain.tables.pojos.PaymentAdditionalInfo; +import dev.vality.newway.domain.tables.pojos.PaymentSessionInfo; +import dev.vality.newway.mapper.Mapper; +import dev.vality.newway.model.InvoicingKey; +import dev.vality.newway.model.PaymentWrapper; +import dev.vality.newway.util.JsonUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Slf4j +@Component +@RequiredArgsConstructor +public class InvoicePaymentSessionChangeStartedMapper implements Mapper { + + private Filter filter = new PathConditionFilter(new PathConditionRule( + "invoice_payment_change.payload.invoice_payment_session_change.payload.session_started", + new IsNullCondition().not())); + + @Override + public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId) { + InvoicePaymentChange invoicePaymentChange = change.getInvoicePaymentChange(); + String invoiceId = event.getSourceId(); + String paymentId = invoicePaymentChange.getId(); + long sequenceId = event.getEventId(); + log.info( + "Start mapping session change started info, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); + PaymentSessionInfo paymentSessionInfo = new PaymentSessionInfo(); + paymentSessionInfo.setEventCreatedAt(TypeUtil.stringToLocalDateTime(event.getCreatedAt())); + paymentSessionInfo.setInvoiceId(invoiceId); + paymentSessionInfo.setPaymentId(paymentId); + paymentSessionInfo.setSequenceId(sequenceId); + paymentSessionInfo.setChangeId(changeId); + paymentSessionInfo.setSessionStatus(PaymentSessionStatus.started); + log.info( + "Payment session started has been mapped, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); + PaymentWrapper paymentWrapper = new PaymentWrapper(); + paymentWrapper.setKey(InvoicingKey.buildKey(invoiceId, paymentId)); + paymentWrapper.setPaymentSessionInfo(paymentSessionInfo); + return paymentWrapper; + } + + @Override + public Filter getFilter() { + return filter; + } +} diff --git a/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeSuspendedMapper.java b/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeSuspendedMapper.java new file mode 100644 index 00000000..df7fe17c --- /dev/null +++ b/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeSuspendedMapper.java @@ -0,0 +1,66 @@ +package dev.vality.newway.mapper.payment.session; + +import dev.vality.damsel.domain.AdditionalTransactionInfo; +import dev.vality.damsel.domain.TransactionInfo; +import dev.vality.damsel.payment_processing.InvoiceChange; +import dev.vality.damsel.payment_processing.InvoicePaymentChange; +import dev.vality.damsel.payment_processing.InvoicePaymentSessionChange; +import dev.vality.damsel.payment_processing.SessionChangePayload; +import dev.vality.geck.common.util.TypeUtil; +import dev.vality.geck.filter.Filter; +import dev.vality.geck.filter.PathConditionFilter; +import dev.vality.geck.filter.condition.IsNullCondition; +import dev.vality.geck.filter.rule.PathConditionRule; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.domain.enums.PaymentSessionStatus; +import dev.vality.newway.domain.tables.pojos.PaymentAdditionalInfo; +import dev.vality.newway.domain.tables.pojos.PaymentSessionInfo; +import dev.vality.newway.mapper.Mapper; +import dev.vality.newway.model.InvoicingKey; +import dev.vality.newway.model.PaymentWrapper; +import dev.vality.newway.util.JsonUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Slf4j +@Component +@RequiredArgsConstructor +public class InvoicePaymentSessionChangeSuspendedMapper implements Mapper { + + private Filter filter = new PathConditionFilter(new PathConditionRule( + "invoice_payment_change.payload.invoice_payment_session_change.payload.session_suspended", + new IsNullCondition().not())); + + @Override + public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer changeId) { + InvoicePaymentChange invoicePaymentChange = change.getInvoicePaymentChange(); + String invoiceId = event.getSourceId(); + String paymentId = invoicePaymentChange.getId(); + long sequenceId = event.getEventId(); + log.info( + "Start mapping session change suspended info, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); + PaymentSessionInfo paymentSessionInfo = new PaymentSessionInfo(); + paymentSessionInfo.setEventCreatedAt(TypeUtil.stringToLocalDateTime(event.getCreatedAt())); + paymentSessionInfo.setInvoiceId(invoiceId); + paymentSessionInfo.setPaymentId(paymentId); + paymentSessionInfo.setSequenceId(sequenceId); + paymentSessionInfo.setChangeId(changeId); + paymentSessionInfo.setSessionStatus(PaymentSessionStatus.activated); + log.info( + "Payment session suspended has been mapped, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", + sequenceId, changeId, invoiceId, paymentId); + PaymentWrapper paymentWrapper = new PaymentWrapper(); + paymentWrapper.setKey(InvoicingKey.buildKey(invoiceId, paymentId)); + paymentWrapper.setPaymentSessionInfo(paymentSessionInfo); + return paymentWrapper; + } + + @Override + public Filter getFilter() { + return filter; + } +} diff --git a/src/main/java/dev/vality/newway/model/PaymentWrapper.java b/src/main/java/dev/vality/newway/model/PaymentWrapper.java index e7336bdd..05481fce 100644 --- a/src/main/java/dev/vality/newway/model/PaymentWrapper.java +++ b/src/main/java/dev/vality/newway/model/PaymentWrapper.java @@ -10,6 +10,7 @@ @AllArgsConstructor public class PaymentWrapper { private Payment payment; + private PaymentSessionInfo paymentSessionInfo; private PaymentStatusInfo paymentStatusInfo; private PaymentPayerInfo paymentPayerInfo; private PaymentAdditionalInfo paymentAdditionalInfo; diff --git a/src/main/resources/db/migration/V10__add_session_status.sql b/src/main/resources/db/migration/V10__add_session_status.sql new file mode 100644 index 00000000..0c98d5f2 --- /dev/null +++ b/src/main/resources/db/migration/V10__add_session_status.sql @@ -0,0 +1,22 @@ +CREATE TYPE dw.payment_session_status AS ENUM ( + 'started', + 'finished', + 'suspended', + 'activated' +); + +CREATE TABLE dw.payment_session_info +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + invoice_id character varying, + payment_id character varying, + sequence_id bigint, + change_id integer, + + session_status dw.payment_session_status NOT NULL, + reason character varying, + + CONSTRAINT payment_session_pk PRIMARY KEY (id), + CONSTRAINT payment_session_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id) +); \ No newline at end of file diff --git a/src/test/java/dev/vality/newway/dao/DaoTests.java b/src/test/java/dev/vality/newway/dao/DaoTests.java index 0b187f36..650f389f 100644 --- a/src/test/java/dev/vality/newway/dao/DaoTests.java +++ b/src/test/java/dev/vality/newway/dao/DaoTests.java @@ -75,6 +75,8 @@ public class DaoTests { @Autowired private PaymentStatusInfoDao paymentStatusInfoDao; @Autowired + private PaymentSessionInfoDao paymentSessionInfoDao; + @Autowired private PaymentPayerInfoDao paymentPayerInfoDao; @Autowired private PaymentAdditionalInfoDao paymentAdditionalInfoDao; diff --git a/src/test/java/dev/vality/newway/service/PaymentWrapperServiceTest.java b/src/test/java/dev/vality/newway/service/PaymentWrapperServiceTest.java index 5dbfceaa..48a5525a 100644 --- a/src/test/java/dev/vality/newway/service/PaymentWrapperServiceTest.java +++ b/src/test/java/dev/vality/newway/service/PaymentWrapperServiceTest.java @@ -37,6 +37,8 @@ public class PaymentWrapperServiceTest { @Autowired private PaymentStatusInfoDao paymentStatusInfoDao; @Autowired + private PaymentSessionInfoDao paymentSessionInfoDao; + @Autowired private PaymentPayerInfoDao paymentPayerInfoDao; @Autowired private PaymentAdditionalInfoDao paymentAdditionalInfoDao; @@ -106,6 +108,7 @@ private void assertPaymentWrapperFromDao(PaymentWrapper expected, String invoice assertEquals(expected.getPaymentRiskData(), paymentRiskDataDao.get(invoiceId, paymentId)); assertEquals(expected.getPaymentFee(), paymentFeeDao.get(invoiceId, paymentId)); assertEquals(expected.getPaymentRoute(), paymentRouteDao.get(invoiceId, paymentId)); + assertEquals(expected.getPaymentSessionInfo(), paymentSessionInfoDao.get(invoiceId, paymentId)); assertEquals(expected.getCashFlowWrapper().getCashFlowLink(), cashFlowLinkDao.get(invoiceId, paymentId)); assertEquals( new HashSet<>(expected.getCashFlowWrapper().getCashFlows()), @@ -123,6 +126,7 @@ private void assertDuplication(String invoiceId, String paymentId) { assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_fee", invoiceId, paymentId, false)); assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_route", invoiceId, paymentId, false)); assertEquals(1, countPaymentEntity(jdbcTemplate, "cash_flow_link", invoiceId, paymentId, false)); + assertEquals(1, countPaymentEntity(jdbcTemplate, "payment_session_info", invoiceId, paymentId, false)); } private void assertTotalDuplication() { @@ -136,6 +140,7 @@ private void assertTotalDuplication() { assertEquals(2, countEntities(jdbcTemplate, "payment_route")); assertEquals(2, countEntities(jdbcTemplate, "cash_flow_link")); assertEquals(6, countEntities(jdbcTemplate, "cash_flow")); + assertEquals(2, countEntities(jdbcTemplate, "payment_session_info")); } @Test diff --git a/src/test/java/dev/vality/newway/utils/PaymentWrapperTestUtil.java b/src/test/java/dev/vality/newway/utils/PaymentWrapperTestUtil.java index 0b0c1c46..bd10b07f 100644 --- a/src/test/java/dev/vality/newway/utils/PaymentWrapperTestUtil.java +++ b/src/test/java/dev/vality/newway/utils/PaymentWrapperTestUtil.java @@ -57,6 +57,10 @@ public static void setInvoiceIdAndPaymentId(PaymentWrapper wrapper, String invoi wrapper.getPaymentRiskData().setInvoiceId(invoiceId); wrapper.getPaymentRiskData().setPaymentId(paymentId); } + if (wrapper.getPaymentSessionInfo() != null) { + wrapper.getPaymentSessionInfo().setInvoiceId(invoiceId); + wrapper.getPaymentSessionInfo().setPaymentId(paymentId); + } if (wrapper.getPaymentFee() != null) { wrapper.getPaymentFee().setInvoiceId(invoiceId); wrapper.getPaymentFee().setPaymentId(paymentId); From cb20cbc7a597de56a878b8b2ee9f590bca0ccb61 Mon Sep 17 00:00:00 2001 From: struga Date: Thu, 15 Jun 2023 10:13:41 +0300 Subject: [PATCH 27/36] Bump damsel (#93) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2099b934..0f7c39f8 100644 --- a/pom.xml +++ b/pom.xml @@ -160,7 +160,7 @@ dev.vality damsel - 1.596-52dbefd + 1.597-bfedcb9 dev.vality From 31a5a90f433cb00221cb6e1707f4d4a826c3a7bc Mon Sep 17 00:00:00 2001 From: struga Date: Thu, 15 Jun 2023 11:10:40 +0300 Subject: [PATCH 28/36] Add status cascad (#94) --- .../InvoicePaymentSessionChangeFinishedMapper.java | 8 +++++++- .../db/migration/V11__add_session_payment_status.sql | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/db/migration/V11__add_session_payment_status.sql diff --git a/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeFinishedMapper.java b/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeFinishedMapper.java index cb448687..273b416d 100644 --- a/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeFinishedMapper.java +++ b/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeFinishedMapper.java @@ -9,6 +9,7 @@ import dev.vality.geck.filter.condition.IsNullCondition; import dev.vality.geck.filter.rule.PathConditionRule; import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.domain.enums.PaymentSessionResult; import dev.vality.newway.domain.enums.PaymentSessionStatus; import dev.vality.newway.domain.tables.pojos.PaymentSessionInfo; import dev.vality.newway.mapper.Mapper; @@ -49,7 +50,12 @@ public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer chan invoicePaymentChange.getPayload().getInvoicePaymentSessionChange().getPayload().getSessionFinished() .getResult(); - paymentSessionInfo.setReason(JsonUtil.thriftBaseToJsonString(result.getFailed())); + if (result.isSetFailed()) { + paymentSessionInfo.setPaymentSessionResult(PaymentSessionResult.failed); + paymentSessionInfo.setReason(JsonUtil.thriftBaseToJsonString(result.getFailed())); + } else { + paymentSessionInfo.setPaymentSessionResult(PaymentSessionResult.succeeded); + } log.info( "Payment session finished has been mapped, sequenceId='{}', changeId='{}', invoiceId='{}', paymentId='{}'", diff --git a/src/main/resources/db/migration/V11__add_session_payment_status.sql b/src/main/resources/db/migration/V11__add_session_payment_status.sql new file mode 100644 index 00000000..40e1cd7a --- /dev/null +++ b/src/main/resources/db/migration/V11__add_session_payment_status.sql @@ -0,0 +1,7 @@ +CREATE TYPE dw.payment_session_result AS ENUM ( + 'failed', + 'succeeded' +); + +ALTER TABLE dw.payment_session_info + ADD COLUMN IF NOT EXISTS payment_session_result dw.payment_session_result NOT NULL; \ No newline at end of file From 9615c92a0abc52063df4131a2b6163e7c0b63c8d Mon Sep 17 00:00:00 2001 From: struga Date: Thu, 15 Jun 2023 11:34:17 +0300 Subject: [PATCH 29/36] Add status cascad (#95) --- .../resources/db/migration/V11__add_session_payment_status.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/db/migration/V11__add_session_payment_status.sql b/src/main/resources/db/migration/V11__add_session_payment_status.sql index 40e1cd7a..3b835771 100644 --- a/src/main/resources/db/migration/V11__add_session_payment_status.sql +++ b/src/main/resources/db/migration/V11__add_session_payment_status.sql @@ -4,4 +4,4 @@ CREATE TYPE dw.payment_session_result AS ENUM ( ); ALTER TABLE dw.payment_session_info - ADD COLUMN IF NOT EXISTS payment_session_result dw.payment_session_result NOT NULL; \ No newline at end of file + ADD COLUMN IF NOT EXISTS payment_session_result dw.payment_session_result; \ No newline at end of file From 672d15a7dfb8bf49a0176d2c21951f06cbdd5cb2 Mon Sep 17 00:00:00 2001 From: struga Date: Thu, 15 Jun 2023 14:12:42 +0300 Subject: [PATCH 30/36] Add terminal (#96) --- .../InvoicePaymentSessionChangeFinishedMapper.java | 9 ++++++++- .../resources/db/migration/V12__add_terminal_session.sql | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/db/migration/V12__add_terminal_session.sql diff --git a/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeFinishedMapper.java b/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeFinishedMapper.java index 273b416d..223d13ed 100644 --- a/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeFinishedMapper.java +++ b/src/main/java/dev/vality/newway/mapper/payment/session/InvoicePaymentSessionChangeFinishedMapper.java @@ -9,8 +9,10 @@ import dev.vality.geck.filter.condition.IsNullCondition; import dev.vality.geck.filter.rule.PathConditionRule; import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.dao.invoicing.iface.PaymentRouteDao; import dev.vality.newway.domain.enums.PaymentSessionResult; import dev.vality.newway.domain.enums.PaymentSessionStatus; +import dev.vality.newway.domain.tables.pojos.PaymentRoute; import dev.vality.newway.domain.tables.pojos.PaymentSessionInfo; import dev.vality.newway.mapper.Mapper; import dev.vality.newway.model.InvoicingKey; @@ -25,6 +27,8 @@ @RequiredArgsConstructor public class InvoicePaymentSessionChangeFinishedMapper implements Mapper { + private final PaymentRouteDao paymentRouteDao; + private Filter filter = new PathConditionFilter(new PathConditionRule( "invoice_payment_change.payload.invoice_payment_session_change.payload.session_finished", new IsNullCondition().not())); @@ -49,7 +53,10 @@ public PaymentWrapper map(InvoiceChange change, MachineEvent event, Integer chan SessionResult result = invoicePaymentChange.getPayload().getInvoicePaymentSessionChange().getPayload().getSessionFinished() .getResult(); - + PaymentRoute paymentRoute = paymentRouteDao.get(invoiceId, paymentId); + if (paymentRoute != null) { + paymentSessionInfo.setPaymentTerminal(paymentRoute.getRouteTerminalId()); + } if (result.isSetFailed()) { paymentSessionInfo.setPaymentSessionResult(PaymentSessionResult.failed); paymentSessionInfo.setReason(JsonUtil.thriftBaseToJsonString(result.getFailed())); diff --git a/src/main/resources/db/migration/V12__add_terminal_session.sql b/src/main/resources/db/migration/V12__add_terminal_session.sql new file mode 100644 index 00000000..ce4293e4 --- /dev/null +++ b/src/main/resources/db/migration/V12__add_terminal_session.sql @@ -0,0 +1,2 @@ +ALTER TABLE dw.payment_session_info + ADD COLUMN IF NOT EXISTS payment_terminal integer; \ No newline at end of file From 76d1b4d7dc549c534f4335fd24200dc451cae52f Mon Sep 17 00:00:00 2001 From: Gregory <32060161+ggmaleva@users.noreply.github.com> Date: Fri, 18 Aug 2023 10:41:08 +0300 Subject: [PATCH 31/36] add withdrawal adjustment (#100) * add withdrawal adjustment * remove redundant * rename * add withdrawal_id + rename fields * add withdrawal adjustment transfer * fix test * fix test * increase test timeout * increase test timeout (2) * increase test timeout (3) * split tests * fix test * fix test * fix test + add logs * fix test * fix test timeout * fix log --------- Co-authored-by: ggmaleva --- pom.xml | 2 +- .../iface/WithdrawalAdjustmentDao.java | 17 ++ .../impl/WithdrawalAdjustmentDaoImpl.java | 66 ++++++ ...AdjustmentMachineEventCopyFactoryImpl.java | 33 +++ .../WithdrawalAdjustmentCreatedHandler.java | 85 +++++++ ...hdrawalAdjustmentStatusChangedHandler.java | 59 +++++ ...rawalAdjustmentTransferCreatedHandler.java | 79 +++++++ ...djustmentTransferStatusChangedHandler.java | 77 +++++++ .../V13__add_withdrawal_adjustment.sql | 37 +++ .../V14__add_new_fistful_cash_flow_type.sql | 1 + src/test/java/dev/vality/newway/TestData.java | 216 ++++++++++++++++++ .../config/PostgresqlJooqSpringBootITest.java | 16 ++ ...ithdrawalAdjustmentCreatedHandlerTest.java | 58 +++++ ...walAdjustmentStatusChangedHandlerTest.java | 58 +++++ ...lAdjustmentTransferCreatedHandlerTest.java | 64 ++++++ ...tmentTransferStatusChangedHandlerTest.java | 67 ++++++ .../vality/newway/kafka/KafkaProducer.java | 6 + ...WithdrawalAdjustmentKafkaListenerTest.java | 83 +++++++ ...alAdjustmentTransferKafkaListenerTest.java | 99 ++++++++ src/test/resources/logback-test.xml | 11 + 20 files changed, 1133 insertions(+), 1 deletion(-) create mode 100644 src/main/java/dev/vality/newway/dao/withdrawal/iface/WithdrawalAdjustmentDao.java create mode 100644 src/main/java/dev/vality/newway/dao/withdrawal/impl/WithdrawalAdjustmentDaoImpl.java create mode 100644 src/main/java/dev/vality/newway/factory/machine/event/WithdrawalAdjustmentMachineEventCopyFactoryImpl.java create mode 100644 src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentCreatedHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentStatusChangedHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferCreatedHandler.java create mode 100644 src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandler.java create mode 100644 src/main/resources/db/migration/V13__add_withdrawal_adjustment.sql create mode 100644 src/main/resources/db/migration/V14__add_new_fistful_cash_flow_type.sql create mode 100644 src/test/java/dev/vality/newway/config/PostgresqlJooqSpringBootITest.java create mode 100644 src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentCreatedHandlerTest.java create mode 100644 src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentStatusChangedHandlerTest.java create mode 100644 src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferCreatedHandlerTest.java create mode 100644 src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandlerTest.java create mode 100644 src/test/java/dev/vality/newway/kafka/WithdrawalAdjustmentKafkaListenerTest.java create mode 100644 src/test/java/dev/vality/newway/kafka/WithdrawalAdjustmentTransferKafkaListenerTest.java create mode 100644 src/test/resources/logback-test.xml diff --git a/pom.xml b/pom.xml index 0f7c39f8..ad3bbe4e 100644 --- a/pom.xml +++ b/pom.xml @@ -165,7 +165,7 @@ dev.vality fistful-proto - 1.145-c45166d + 1.159-936ed9a dev.vality diff --git a/src/main/java/dev/vality/newway/dao/withdrawal/iface/WithdrawalAdjustmentDao.java b/src/main/java/dev/vality/newway/dao/withdrawal/iface/WithdrawalAdjustmentDao.java new file mode 100644 index 00000000..c8793b43 --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/withdrawal/iface/WithdrawalAdjustmentDao.java @@ -0,0 +1,17 @@ +package dev.vality.newway.dao.withdrawal.iface; + +import dev.vality.dao.GenericDao; +import dev.vality.newway.domain.tables.pojos.WithdrawalAdjustment; +import dev.vality.newway.exception.DaoException; + +import java.util.Optional; + +public interface WithdrawalAdjustmentDao extends GenericDao { + + Optional save(WithdrawalAdjustment withdrawalAdjustment) throws DaoException; + + WithdrawalAdjustment getByIds(String withdrawalId, String withdrawalAdjustmentId) throws DaoException; + + void updateNotCurrent(Long withdrawalAdjustmentId) throws DaoException; + +} diff --git a/src/main/java/dev/vality/newway/dao/withdrawal/impl/WithdrawalAdjustmentDaoImpl.java b/src/main/java/dev/vality/newway/dao/withdrawal/impl/WithdrawalAdjustmentDaoImpl.java new file mode 100644 index 00000000..7338a65d --- /dev/null +++ b/src/main/java/dev/vality/newway/dao/withdrawal/impl/WithdrawalAdjustmentDaoImpl.java @@ -0,0 +1,66 @@ +package dev.vality.newway.dao.withdrawal.impl; + +import dev.vality.dao.impl.AbstractGenericDao; +import dev.vality.mapper.RecordRowMapper; +import dev.vality.newway.dao.withdrawal.iface.WithdrawalAdjustmentDao; +import dev.vality.newway.domain.tables.pojos.WithdrawalAdjustment; +import dev.vality.newway.domain.tables.records.WithdrawalAdjustmentRecord; +import dev.vality.newway.exception.DaoException; +import dev.vality.newway.exception.NotFoundException; +import org.jooq.Query; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.Optional; + +import static dev.vality.newway.domain.tables.WithdrawalAdjustment.WITHDRAWAL_ADJUSTMENT; + +@Component +public class WithdrawalAdjustmentDaoImpl extends AbstractGenericDao implements WithdrawalAdjustmentDao { + + private final RowMapper withdrawalAdjustmentRowMapper; + + @Autowired + public WithdrawalAdjustmentDaoImpl(DataSource dataSource) { + super(dataSource); + withdrawalAdjustmentRowMapper = new RecordRowMapper<>(WITHDRAWAL_ADJUSTMENT, WithdrawalAdjustment.class); + } + + @Override + public Optional save(WithdrawalAdjustment withdrawalAdjustment) throws DaoException { + WithdrawalAdjustmentRecord adjustmentRecord = getDslContext().newRecord(WITHDRAWAL_ADJUSTMENT, withdrawalAdjustment); + Query query = getDslContext() + .insertInto(WITHDRAWAL_ADJUSTMENT) + .set(adjustmentRecord) + .onConflict(WITHDRAWAL_ADJUSTMENT.ADJUSTMENT_ID, WITHDRAWAL_ADJUSTMENT.SEQUENCE_ID) + .doNothing() + .returning(WITHDRAWAL_ADJUSTMENT.ID); + + GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); + execute(query, keyHolder); + return Optional.ofNullable(keyHolder.getKey()).map(Number::longValue); + } + + @Override + public WithdrawalAdjustment getByIds(String withdrawalId, String withdrawalAdjustmentId) throws DaoException { + Query query = getDslContext().selectFrom(WITHDRAWAL_ADJUSTMENT) + .where(WITHDRAWAL_ADJUSTMENT.ADJUSTMENT_ID.eq(withdrawalAdjustmentId) + .and(WITHDRAWAL_ADJUSTMENT.WITHDRAWAL_ID.eq(withdrawalId)) + .and(WITHDRAWAL_ADJUSTMENT.CURRENT)); + return Optional.ofNullable(fetchOne(query, withdrawalAdjustmentRowMapper)) + .orElseThrow(() -> new NotFoundException( + String.format("WithdrawalAdjustment not found, withdrawalAdjustmentId='%s', withdrawalId='%s'", + withdrawalAdjustmentId, withdrawalId))); + } + + @Override + public void updateNotCurrent(Long id) throws DaoException { + Query query = getDslContext().update(WITHDRAWAL_ADJUSTMENT).set(WITHDRAWAL_ADJUSTMENT.CURRENT, false) + .where(WITHDRAWAL_ADJUSTMENT.ID.eq(id) + .and(WITHDRAWAL_ADJUSTMENT.CURRENT)); + execute(query); + } +} diff --git a/src/main/java/dev/vality/newway/factory/machine/event/WithdrawalAdjustmentMachineEventCopyFactoryImpl.java b/src/main/java/dev/vality/newway/factory/machine/event/WithdrawalAdjustmentMachineEventCopyFactoryImpl.java new file mode 100644 index 00000000..7ed3c7ea --- /dev/null +++ b/src/main/java/dev/vality/newway/factory/machine/event/WithdrawalAdjustmentMachineEventCopyFactoryImpl.java @@ -0,0 +1,33 @@ +package dev.vality.newway.factory.machine.event; + +import dev.vality.geck.common.util.TypeUtil; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.domain.tables.pojos.WithdrawalAdjustment; +import org.springframework.stereotype.Component; + +@Component +public class WithdrawalAdjustmentMachineEventCopyFactoryImpl implements MachineEventCopyFactory { + + @Override + public WithdrawalAdjustment create(MachineEvent event, Long sequenceId, String id, WithdrawalAdjustment old, String occurredAt) { + WithdrawalAdjustment withdrawalAdjustment = null; + if (old != null) { + withdrawalAdjustment = new WithdrawalAdjustment(old); + } else { + withdrawalAdjustment = new WithdrawalAdjustment(); + } + withdrawalAdjustment.setId(null); + withdrawalAdjustment.setWtime(null); + withdrawalAdjustment.setAdjustmentId(id); + withdrawalAdjustment.setSequenceId(sequenceId); + withdrawalAdjustment.setEventCreatedAt(TypeUtil.stringToLocalDateTime(event.getCreatedAt())); + withdrawalAdjustment.setEventOccuredAt(TypeUtil.stringToLocalDateTime(occurredAt)); + return withdrawalAdjustment; + } + + @Override + public WithdrawalAdjustment create(MachineEvent event, Long sequenceId, String id, String occurredAt) { + return create(event, sequenceId, id, null, occurredAt); + } + +} diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentCreatedHandler.java new file mode 100644 index 00000000..0c3c67a3 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentCreatedHandler.java @@ -0,0 +1,85 @@ +package dev.vality.newway.handler.event.stock.impl.withdrawal; + +import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.fistful.withdrawal.adjustment.Adjustment; +import dev.vality.fistful.withdrawal.adjustment.CashFlowChangePlan; +import dev.vality.fistful.withdrawal.adjustment.ChangesPlan; +import dev.vality.fistful.withdrawal.adjustment.DataRevisionChangePlan; +import dev.vality.fistful.withdrawal.status.Status; +import dev.vality.geck.common.util.TBaseUtil; +import dev.vality.geck.filter.Filter; +import dev.vality.geck.filter.PathConditionFilter; +import dev.vality.geck.filter.condition.IsNullCondition; +import dev.vality.geck.filter.rule.PathConditionRule; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.dao.withdrawal.iface.WithdrawalAdjustmentDao; +import dev.vality.newway.domain.enums.WithdrawalAdjustmentStatus; +import dev.vality.newway.domain.enums.WithdrawalAdjustmentType; +import dev.vality.newway.domain.enums.WithdrawalStatus; +import dev.vality.newway.domain.tables.pojos.WithdrawalAdjustment; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; +import dev.vality.newway.util.FistfulCashFlowUtil; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class WithdrawalAdjustmentCreatedHandler implements WithdrawalHandler { + + private final WithdrawalAdjustmentDao withdrawalAdjustmentDao; + private final MachineEventCopyFactory machineEventCopyFactory; + + @Getter + private final Filter filter = + new PathConditionFilter(new PathConditionRule("change.adjustment.payload.created.adjustment", new IsNullCondition().not())); + + @Override + public void handle(TimestampedChange timestampedChange, MachineEvent event) { + Adjustment adjustmentDamsel = timestampedChange.getChange().getAdjustment().getPayload().getCreated().getAdjustment(); + long sequenceId = event.getEventId(); + String withdrawalId = event.getSourceId(); + String withdrawalAdjustmentId = adjustmentDamsel.getId(); + log.info("Start withdrawal adjustment created handling, sequenceId={}, withdrawalId={}, withdrawalAdjustmentId={}", + sequenceId, withdrawalId, withdrawalAdjustmentId); + + WithdrawalAdjustment withdrawalAdjustment = + machineEventCopyFactory.create(event, sequenceId, withdrawalAdjustmentId, timestampedChange.getOccuredAt()); + withdrawalAdjustment.setExternalId(adjustmentDamsel.getExternalId()); + withdrawalAdjustment.setStatus(WithdrawalAdjustmentStatus.pending); + withdrawalAdjustment.setPartyRevision(adjustmentDamsel.getPartyRevision()); + withdrawalAdjustment.setWithdrawalId(withdrawalId); + ChangesPlan changesPlan = adjustmentDamsel.getChangesPlan(); + if (changesPlan.isSetNewStatus()) { + Status newStatus = changesPlan.getNewStatus().getNewStatus(); + withdrawalAdjustment.setType(WithdrawalAdjustmentType.status_change); + withdrawalAdjustment.setWithdrawalStatus(TBaseUtil.unionFieldToEnum(newStatus, WithdrawalStatus.class)); + } else if (changesPlan.isSetNewDomainRevision()) { + DataRevisionChangePlan newDomainRevision = changesPlan.getNewDomainRevision(); + withdrawalAdjustment.setType(WithdrawalAdjustmentType.domain_revision); + withdrawalAdjustment.setDomainRevision(newDomainRevision.getNewDomainRevision()); + } + if (changesPlan.isSetNewCashFlow()) { + CashFlowChangePlan cashFlow = changesPlan.getNewCashFlow(); + long amount = computeAmount(cashFlow); + withdrawalAdjustment.setAmount(amount); + withdrawalAdjustment + .setProviderFee(FistfulCashFlowUtil.getFistfulProviderFee(cashFlow.getNewCashFlow().getPostings())); + withdrawalAdjustment.setFee(FistfulCashFlowUtil.getFistfulFee(cashFlow.getNewCashFlow().getPostings())); + } + withdrawalAdjustmentDao.save(withdrawalAdjustment).ifPresentOrElse( + id -> log.info("withdrawalAdjustment created has been saved, sequenceId={}, withdrawalId={}, withdrawalAdjustmentId={}", + sequenceId, withdrawalId, withdrawalAdjustmentId), + () -> log.info("withdrawalAdjustment created duplicated, sequenceId={}, withdrawalId={}, withdrawalAdjustmentId={}", + sequenceId, withdrawalId, withdrawalAdjustmentId)); + } + + private long computeAmount(CashFlowChangePlan cashFlow) { + Long oldAmount = FistfulCashFlowUtil.computeAmount(cashFlow.getOldCashFlowInverted().getPostings()); + Long newAmount = FistfulCashFlowUtil.computeAmount(cashFlow.getNewCashFlow().getPostings()); + return newAmount + oldAmount; + } + +} diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentStatusChangedHandler.java new file mode 100644 index 00000000..0bc101d5 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentStatusChangedHandler.java @@ -0,0 +1,59 @@ +package dev.vality.newway.handler.event.stock.impl.withdrawal; + +import dev.vality.fistful.withdrawal.AdjustmentChange; +import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.fistful.withdrawal.adjustment.Status; +import dev.vality.geck.common.util.TBaseUtil; +import dev.vality.geck.filter.Filter; +import dev.vality.geck.filter.PathConditionFilter; +import dev.vality.geck.filter.condition.IsNullCondition; +import dev.vality.geck.filter.rule.PathConditionRule; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.dao.withdrawal.iface.WithdrawalAdjustmentDao; +import dev.vality.newway.domain.enums.WithdrawalAdjustmentStatus; +import dev.vality.newway.domain.tables.pojos.WithdrawalAdjustment; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class WithdrawalAdjustmentStatusChangedHandler implements WithdrawalHandler { + + private final WithdrawalAdjustmentDao withdrawalAdjustmentDao; + private final MachineEventCopyFactory machineEventCopyFactory; + + @Getter + private final Filter filter = + new PathConditionFilter(new PathConditionRule("change.adjustment.payload.status_changed.status", new IsNullCondition().not())); + + @Override + public void handle(TimestampedChange timestampedChange, MachineEvent event) { + AdjustmentChange adjustmentChange = timestampedChange.getChange().getAdjustment(); + Status status = adjustmentChange.getPayload().getStatusChanged().getStatus(); + long sequenceId = event.getEventId(); + String withdrawalId = event.getSourceId(); + String withdrawalAdjustmentId = adjustmentChange.getId(); + log.info("Start withdrawal adjustment status changed handling, " + + "sequenceId={}, withdrawalId={}, withdrawalAdjustmentId={}", + sequenceId, withdrawalId, withdrawalAdjustmentId); + final var withdrawalAdjustmentOld = withdrawalAdjustmentDao.getByIds(withdrawalId, withdrawalAdjustmentId); + var withdrawalAdjustmentNew = machineEventCopyFactory + .create(event, sequenceId, withdrawalAdjustmentId, withdrawalAdjustmentOld, timestampedChange.getOccuredAt()); + withdrawalAdjustmentNew.setStatus(TBaseUtil.unionFieldToEnum(status, WithdrawalAdjustmentStatus.class)); + withdrawalAdjustmentDao.save(withdrawalAdjustmentNew).ifPresentOrElse( + id -> { + Long oldId = withdrawalAdjustmentOld.getId(); + log.info("Update not current for withdrawal adjustment with id={}", oldId); + withdrawalAdjustmentDao.updateNotCurrent(oldId); + log.info("WithdrawalAdjustment status have been changed, sequenceId={}, withdrawalId={}, withdrawalAdjustmentId={}", + sequenceId, withdrawalId, withdrawalAdjustmentId); + }, + () -> log.info("WithdrawalAdjustment status have been changed, sequenceId={}, withdrawalId={}, withdrawalAdjustmentId={}", + sequenceId, withdrawalId, withdrawalAdjustmentId)); + } + +} diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferCreatedHandler.java new file mode 100644 index 00000000..f011ab82 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferCreatedHandler.java @@ -0,0 +1,79 @@ +package dev.vality.newway.handler.event.stock.impl.withdrawal; + +import dev.vality.fistful.cashflow.FinalCashFlowPosting; +import dev.vality.fistful.withdrawal.Change; +import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.fistful.withdrawal.adjustment.TransferChange; +import dev.vality.geck.filter.Filter; +import dev.vality.geck.filter.PathConditionFilter; +import dev.vality.geck.filter.condition.IsNullCondition; +import dev.vality.geck.filter.rule.PathConditionRule; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.dao.withdrawal.iface.FistfulCashFlowDao; +import dev.vality.newway.dao.withdrawal.iface.WithdrawalAdjustmentDao; +import dev.vality.newway.domain.enums.FistfulCashFlowChangeType; +import dev.vality.newway.domain.enums.WithdrawalTransferStatus; +import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; +import dev.vality.newway.domain.tables.pojos.WithdrawalAdjustment; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; +import dev.vality.newway.util.FistfulCashFlowUtil; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +public class WithdrawalAdjustmentTransferCreatedHandler implements WithdrawalHandler { + + private final WithdrawalAdjustmentDao withdrawalAdjustmentDao; + private final FistfulCashFlowDao fistfulCashFlowDao; + private final MachineEventCopyFactory machineEventCopyFactory; + + @Getter + private final Filter filter = new PathConditionFilter( + new PathConditionRule( + "change.adjustment.payload.transfer.payload.created", + new IsNullCondition().not()) + ); + + @Override + public void handle(TimestampedChange timestampedChange, MachineEvent event) { + Change change = timestampedChange.getChange(); + long sequenceId = event.getEventId(); + String withdrawalId = event.getSourceId(); + String withdrawalAdjustmentId = change.getAdjustment().getId(); + TransferChange transferChange = change.getAdjustment().getPayload().getTransfer(); + log.info("Start withdrawal adjustment transfer created handling, " + + "sequenceId={}, withdrawalId={}, withdrawalAdjustmentId={}", + sequenceId, withdrawalId, withdrawalAdjustmentId); + log.debug("Transfer={}", transferChange); + final var withdrawalAdjustmentOld = withdrawalAdjustmentDao.getByIds(withdrawalId, withdrawalAdjustmentId); + var withdrawalAdjustmentNew = machineEventCopyFactory + .create(event, sequenceId, withdrawalAdjustmentId, withdrawalAdjustmentOld, timestampedChange.getOccuredAt()); + + withdrawalAdjustmentNew.setWithdrawalTransferStatus(WithdrawalTransferStatus.created); + List postings = + transferChange.getPayload().getCreated().getTransfer().getCashflow().getPostings(); + withdrawalAdjustmentNew.setFee(FistfulCashFlowUtil.getFistfulFee(postings)); + withdrawalAdjustmentNew.setProviderFee(FistfulCashFlowUtil.getFistfulProviderFee(postings)); + + withdrawalAdjustmentDao.save(withdrawalAdjustmentNew).ifPresentOrElse( + id -> { + Long oldId = withdrawalAdjustmentOld.getId(); + log.info("Update not current for withdrawal adjustment with id={}", oldId); + withdrawalAdjustmentDao.updateNotCurrent(oldId); + List fistfulCashFlows = FistfulCashFlowUtil + .convertFistfulCashFlows(postings, id, FistfulCashFlowChangeType.withdrawal_adjustment); + fistfulCashFlowDao.save(fistfulCashFlows); + log.info("Withdrawal adjustment transfer have been changed, sequenceId={}, withdrawalId={}, withdrawalAdjustmentId={}", + sequenceId, withdrawalId, withdrawalAdjustmentId); + }, + () -> log.info("Withdrawal adjustment transfer have been changed, sequenceId={}, withdrawalId={}, withdrawalAdjustmentId={}", + sequenceId, withdrawalId, withdrawalAdjustmentId)); + } + +} diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandler.java new file mode 100644 index 00000000..1d14d2f4 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandler.java @@ -0,0 +1,77 @@ +package dev.vality.newway.handler.event.stock.impl.withdrawal; + +import dev.vality.fistful.transfer.Status; +import dev.vality.fistful.withdrawal.Change; +import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.fistful.withdrawal.adjustment.TransferChange; +import dev.vality.geck.common.util.TBaseUtil; +import dev.vality.geck.filter.Filter; +import dev.vality.geck.filter.PathConditionFilter; +import dev.vality.geck.filter.condition.IsNullCondition; +import dev.vality.geck.filter.rule.PathConditionRule; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.dao.withdrawal.iface.FistfulCashFlowDao; +import dev.vality.newway.dao.withdrawal.iface.WithdrawalAdjustmentDao; +import dev.vality.newway.domain.enums.FistfulCashFlowChangeType; +import dev.vality.newway.domain.enums.WithdrawalTransferStatus; +import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; +import dev.vality.newway.domain.tables.pojos.WithdrawalAdjustment; +import dev.vality.newway.factory.machine.event.MachineEventCopyFactory; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +public class WithdrawalAdjustmentTransferStatusChangedHandler implements WithdrawalHandler { + + private final WithdrawalAdjustmentDao withdrawalAdjustmentDao; + private final FistfulCashFlowDao fistfulCashFlowDao; + private final MachineEventCopyFactory machineEventCopyFactory; + + @Getter + private final Filter filter = new PathConditionFilter( + new PathConditionRule( + "change.adjustment.payload.transfer.payload.status_changed.status", + new IsNullCondition().not())); + + @Override + public void handle(TimestampedChange timestampedChange, MachineEvent event) { + Change change = timestampedChange.getChange(); + TransferChange transferChange = change.getAdjustment().getPayload().getTransfer(); + Status status = transferChange.getPayload().getStatusChanged().getStatus(); + long sequenceId = event.getEventId(); + String withdrawalId = event.getSourceId(); + String withdrawalAdjustmentId = change.getAdjustment().getId(); + log.info("Start withdrawal adjustment transfer status changed handling, sequenceId={}, withdrawalId={}, withdrawalAdjustmentId={}, transfer={}", + sequenceId, withdrawalId, withdrawalAdjustmentId, transferChange); + + final WithdrawalAdjustment withdrawalAdjustmentOld = withdrawalAdjustmentDao.getByIds(withdrawalId, withdrawalAdjustmentId); + WithdrawalAdjustment withdrawalAdjustmentNew = machineEventCopyFactory + .create(event, sequenceId, withdrawalId, withdrawalAdjustmentOld, timestampedChange.getOccuredAt()); + withdrawalAdjustmentNew.setWithdrawalTransferStatus(TBaseUtil.unionFieldToEnum(status, WithdrawalTransferStatus.class)); + + withdrawalAdjustmentDao.save(withdrawalAdjustmentNew).ifPresentOrElse( + id -> { + Long oldId = withdrawalAdjustmentOld.getId(); + log.info("Update not current for withdrawal adjustment with id={}", oldId); + withdrawalAdjustmentDao.updateNotCurrent(oldId); + List cashFlows = + fistfulCashFlowDao.getByObjId(oldId, FistfulCashFlowChangeType.withdrawal_adjustment); + cashFlows.forEach(pcf -> { + pcf.setId(null); + pcf.setObjId(id); + }); + fistfulCashFlowDao.save(cashFlows); + log.info("Withdrawal adjustment transfer status have been changed, sequenceId={}, withdrawalId={}, withdrawalAdjustmentId={}", + sequenceId, withdrawalId, withdrawalAdjustmentId); + }, + () -> log.info("Withdrawal adjustment transfer have been changed, sequenceId={}, withdrawalId={}, withdrawalAdjustmentId={}", + sequenceId, withdrawalId, withdrawalAdjustmentId)); + } + +} diff --git a/src/main/resources/db/migration/V13__add_withdrawal_adjustment.sql b/src/main/resources/db/migration/V13__add_withdrawal_adjustment.sql new file mode 100644 index 00000000..41b9bcc8 --- /dev/null +++ b/src/main/resources/db/migration/V13__add_withdrawal_adjustment.sql @@ -0,0 +1,37 @@ +CREATE TYPE dw.withdrawal_adjustment_status AS ENUM ( + 'pending', + 'succeeded' + ); + +CREATE TYPE dw.withdrawal_adjustment_type AS ENUM ( + 'status_change', + 'domain_revision' + ); + +CREATE TABLE dw.withdrawal_adjustment +( + id bigserial NOT NULL, + event_created_at timestamp without time zone NOT NULL, + event_occured_at timestamp without time zone NOT NULL, + sequence_id bigint NOT NULL, + adjustment_id character varying NOT NULL, + domain_revision bigint, + withdrawal_status dw.withdrawal_status, + party_revision bigint NOT NULL, + withdrawal_id character varying NOT NULL, + status dw.withdrawal_adjustment_status NOT NULL, + type dw.withdrawal_adjustment_type NOT NULL, + withdrawal_transfer_status dw.withdrawal_transfer_status, + wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + external_id character varying, + amount bigint, + fee bigint, + provider_fee bigint, + current boolean DEFAULT true NOT NULL, + CONSTRAINT withdrawal_adjustment_pkey PRIMARY KEY (id), + CONSTRAINT withdrawal_adjustment_uniq UNIQUE (adjustment_id, sequence_id) +); + +CREATE INDEX withdrawal_adjustment_event_created_at_idx ON dw.withdrawal_adjustment USING btree (event_created_at); +CREATE INDEX withdrawal_adjustment_event_occured_at_idx ON dw.withdrawal_adjustment USING btree (event_occured_at); +CREATE INDEX withdrawal_adjustment_id_idx ON dw.withdrawal_adjustment USING btree (adjustment_id); diff --git a/src/main/resources/db/migration/V14__add_new_fistful_cash_flow_type.sql b/src/main/resources/db/migration/V14__add_new_fistful_cash_flow_type.sql new file mode 100644 index 00000000..3a603f47 --- /dev/null +++ b/src/main/resources/db/migration/V14__add_new_fistful_cash_flow_type.sql @@ -0,0 +1 @@ +ALTER TYPE dw.fistful_cash_flow_change_type ADD VALUE 'withdrawal_adjustment'; diff --git a/src/test/java/dev/vality/newway/TestData.java b/src/test/java/dev/vality/newway/TestData.java index 2a929421..09cf511e 100644 --- a/src/test/java/dev/vality/newway/TestData.java +++ b/src/test/java/dev/vality/newway/TestData.java @@ -3,11 +3,27 @@ import dev.vality.damsel.domain.InvoicePaymentChargeback; import dev.vality.damsel.domain.*; import dev.vality.damsel.payment_processing.*; +import dev.vality.fistful.cashflow.FinalCashFlow; +import dev.vality.fistful.transfer.Committed; +import dev.vality.fistful.transfer.Transfer; +import dev.vality.fistful.withdrawal.AdjustmentChange; +import dev.vality.fistful.withdrawal.Change; +import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.fistful.withdrawal.adjustment.*; import dev.vality.geck.common.util.TypeUtil; +import dev.vality.kafka.common.serialization.ThriftSerializer; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.machinegun.msgpack.Value; +import dev.vality.newway.domain.enums.FistfulCashFlowChangeType; +import dev.vality.newway.domain.enums.WithdrawalAdjustmentStatus; +import dev.vality.newway.domain.enums.WithdrawalAdjustmentType; +import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; +import dev.vality.newway.domain.tables.pojos.WithdrawalAdjustment; import lombok.AccessLevel; import lombok.NoArgsConstructor; import java.time.Instant; +import java.time.LocalDateTime; import java.util.Collections; import java.util.List; import java.util.Set; @@ -271,4 +287,204 @@ public static Cash createTestCash(long amount) { return new Cash(amount, new CurrencyRef("RUB")); } + public static TimestampedChange createWithdrawalAdjustmentCreatedChange(String id) { + Adjustment adjustment = new Adjustment(); + adjustment.setId(id); + adjustment.setOperationTimestamp("2023-07-03T10:15:30Z"); + adjustment.setCreatedAt("2023-07-03T10:15:30Z"); + adjustment.setStatus(Status.pending(new Pending())); + var newStatus = new dev.vality.fistful.withdrawal.status.Status(); + newStatus.setPending(new dev.vality.fistful.withdrawal.status.Pending()); + adjustment.setChangesPlan( + new ChangesPlan() + .setNewStatus(new StatusChangePlan().setNewStatus(newStatus)) + ); + var payload = new dev.vality.fistful.withdrawal.adjustment.Change(); + payload.setCreated(new CreatedChange().setAdjustment(adjustment)); + AdjustmentChange adjustmentChange = new AdjustmentChange(); + adjustmentChange.setId("id"); + adjustmentChange.setPayload(payload); + Change change = new Change(); + change.setAdjustment(adjustmentChange); + TimestampedChange timestampedChange = new TimestampedChange(); + timestampedChange.setOccuredAt("2023-07-03T10:15:30Z"); + timestampedChange.setChange(change); + return timestampedChange; + } + + public static TimestampedChange createWithdrawalAdjustmentCreatedDomainRevisionChange(String id) { + Adjustment adjustment = new Adjustment(); + adjustment.setId(id); + adjustment.setOperationTimestamp("2023-07-03T10:15:30Z"); + adjustment.setCreatedAt("2023-07-03T10:15:30Z"); + adjustment.setStatus(Status.pending(new Pending())); + var newStatus = new dev.vality.fistful.withdrawal.status.Status(); + newStatus.setPending(new dev.vality.fistful.withdrawal.status.Pending()); + adjustment.setChangesPlan( + new ChangesPlan() + .setNewDomainRevision(new DataRevisionChangePlan().setNewDomainRevision(1L)) + ); + var payload = new dev.vality.fistful.withdrawal.adjustment.Change(); + payload.setCreated(new CreatedChange().setAdjustment(adjustment)); + AdjustmentChange adjustmentChange = new AdjustmentChange(); + adjustmentChange.setId("id"); + adjustmentChange.setPayload(payload); + Change change = new Change(); + change.setAdjustment(adjustmentChange); + TimestampedChange timestampedChange = new TimestampedChange(); + timestampedChange.setOccuredAt("2023-07-03T10:15:30Z"); + timestampedChange.setChange(change); + return timestampedChange; + } + + public static TimestampedChange createWithdrawalAdjustmentStatusChange(String id) { + var payload = new dev.vality.fistful.withdrawal.adjustment.Change(); + payload.setStatusChanged(new StatusChange(Status.succeeded(new Succeeded()))); + AdjustmentChange adjustmentChange = new AdjustmentChange(); + adjustmentChange.setId(id); + adjustmentChange.setPayload(payload); + Change change = new Change(); + change.setAdjustment(adjustmentChange); + TimestampedChange timestampedChange = new TimestampedChange(); + timestampedChange.setOccuredAt("2023-07-03T10:15:30Z"); + timestampedChange.setChange(change); + return timestampedChange; + } + + public static TimestampedChange createWithdrawalAdjustmentTransferCreatedChange(String id) { + Transfer transfer = new Transfer(); + transfer.setId("id"); + List postings = getFinalCashFlowPostings(); + transfer.setCashflow(new FinalCashFlow().setPostings(postings)); + dev.vality.fistful.transfer.CreatedChange createdChange = new dev.vality.fistful.transfer.CreatedChange(); + createdChange.setTransfer(transfer); + var payload = new dev.vality.fistful.withdrawal.adjustment.Change(); + payload.setTransfer(new TransferChange(dev.vality.fistful.transfer.Change.created(createdChange))); + AdjustmentChange adjustmentChange = new AdjustmentChange(); + adjustmentChange.setId(id); + adjustmentChange.setPayload(payload); + Change change = new Change(); + change.setAdjustment(adjustmentChange); + TimestampedChange timestampedChange = new TimestampedChange(); + timestampedChange.setOccuredAt("2023-07-03T10:15:30Z"); + timestampedChange.setChange(change); + return timestampedChange; + } + + private static List getFinalCashFlowPostings() { + dev.vality.fistful.cashflow.FinalCashFlowPosting fistfulPosting = new dev.vality.fistful.cashflow.FinalCashFlowPosting(); + fistfulPosting.setDestination( + new dev.vality.fistful.cashflow.FinalCashFlowAccount() + .setAccountId("1") + .setAccountType(dev.vality.fistful.cashflow.CashFlowAccount.system( + dev.vality.fistful.cashflow.SystemCashFlowAccount.settlement))); + fistfulPosting.setSource(new dev.vality.fistful.cashflow.FinalCashFlowAccount() + .setAccountId("2") + .setAccountType(dev.vality.fistful.cashflow.CashFlowAccount.wallet( + dev.vality.fistful.cashflow.WalletCashFlowAccount.receiver_destination))); + fistfulPosting.setVolume(new dev.vality.fistful.base.Cash() + .setAmount(100L) + .setCurrency(new dev.vality.fistful.base.CurrencyRef("RUB"))); + dev.vality.fistful.cashflow.FinalCashFlowPosting providerPosting = new dev.vality.fistful.cashflow.FinalCashFlowPosting(); + providerPosting.setDestination( + new dev.vality.fistful.cashflow.FinalCashFlowAccount() + .setAccountId("3") + .setAccountType(dev.vality.fistful.cashflow.CashFlowAccount.provider( + dev.vality.fistful.cashflow.ProviderCashFlowAccount.settlement))); + providerPosting.setSource(new dev.vality.fistful.cashflow.FinalCashFlowAccount() + .setAccountId("4") + .setAccountType(dev.vality.fistful.cashflow.CashFlowAccount.system( + dev.vality.fistful.cashflow.SystemCashFlowAccount.settlement))); + providerPosting.setVolume(new dev.vality.fistful.base.Cash() + .setAmount(100L) + .setCurrency(new dev.vality.fistful.base.CurrencyRef("RUB"))); + dev.vality.fistful.cashflow.FinalCashFlowPosting merchantSourcePosting = new dev.vality.fistful.cashflow.FinalCashFlowPosting(); + merchantSourcePosting.setDestination( + new dev.vality.fistful.cashflow.FinalCashFlowAccount() + .setAccountId("5") + .setAccountType(dev.vality.fistful.cashflow.CashFlowAccount.provider( + dev.vality.fistful.cashflow.ProviderCashFlowAccount.settlement))); + merchantSourcePosting.setSource(new dev.vality.fistful.cashflow.FinalCashFlowAccount() + .setAccountId("6") + .setAccountType(dev.vality.fistful.cashflow.CashFlowAccount.merchant( + dev.vality.fistful.cashflow.MerchantCashFlowAccount.settlement))); + merchantSourcePosting.setVolume(new dev.vality.fistful.base.Cash() + .setAmount(200L) + .setCurrency(new dev.vality.fistful.base.CurrencyRef("RUB"))); + dev.vality.fistful.cashflow.FinalCashFlowPosting merchantDestinationPosting = new dev.vality.fistful.cashflow.FinalCashFlowPosting(); + merchantDestinationPosting.setDestination( + new dev.vality.fistful.cashflow.FinalCashFlowAccount() + .setAccountId("7") + .setAccountType(dev.vality.fistful.cashflow.CashFlowAccount.merchant( + dev.vality.fistful.cashflow.MerchantCashFlowAccount.settlement))); + merchantDestinationPosting.setSource(new dev.vality.fistful.cashflow.FinalCashFlowAccount() + .setAccountId("8") + .setAccountType(dev.vality.fistful.cashflow.CashFlowAccount.merchant( + dev.vality.fistful.cashflow.MerchantCashFlowAccount.settlement))); + merchantDestinationPosting.setVolume(new dev.vality.fistful.base.Cash() + .setAmount(500) + .setCurrency(new dev.vality.fistful.base.CurrencyRef("RUB"))); + return List.of( + fistfulPosting, + providerPosting, + merchantSourcePosting, + merchantDestinationPosting); + } + + public static TimestampedChange createWithdrawalAdjustmentTransferStatusChange(String id) { + dev.vality.fistful.transfer.StatusChange statusChange = new dev.vality.fistful.transfer.StatusChange(); + statusChange.setStatus(dev.vality.fistful.transfer.Status.committed(new Committed())); + var payload = new dev.vality.fistful.withdrawal.adjustment.Change(); + payload.setTransfer(new TransferChange(dev.vality.fistful.transfer.Change.status_changed(statusChange))); + AdjustmentChange adjustmentChange = new AdjustmentChange(); + adjustmentChange.setId(id); + adjustmentChange.setPayload(payload); + Change change = new Change(); + change.setAdjustment(adjustmentChange); + TimestampedChange timestampedChange = new TimestampedChange(); + timestampedChange.setOccuredAt("2023-07-03T10:15:30Z"); + timestampedChange.setChange(change); + return timestampedChange; + } + + public static MachineEvent createWithdrawalAdjustmentdMachineEvent(TimestampedChange timestampedChange) { + return new MachineEvent() + .setEventId(2L) + .setSourceId("sourceId") + .setSourceNs("2") + .setCreatedAt("2021-05-31T06:12:27Z") + .setData(Value.bin(new ThriftSerializer<>().serialize("", timestampedChange))); + } + + public static WithdrawalAdjustment createWithdrawalAdjustment(String id) { + WithdrawalAdjustment withdrawalAdjustment = new WithdrawalAdjustment(); + withdrawalAdjustment.setType(WithdrawalAdjustmentType.domain_revision); + withdrawalAdjustment.setStatus(WithdrawalAdjustmentStatus.pending); + withdrawalAdjustment.setSequenceId(1L); + withdrawalAdjustment.setAdjustmentId(id); + withdrawalAdjustment.setDomainRevision(1L); + withdrawalAdjustment.setCurrent(true); + withdrawalAdjustment.setPartyRevision(2L); + withdrawalAdjustment.setWithdrawalId("withdrawalId"); + withdrawalAdjustment.setExternalId("id"); + withdrawalAdjustment.setEventOccuredAt(LocalDateTime.now()); + withdrawalAdjustment.setEventCreatedAt(LocalDateTime.now()); + withdrawalAdjustment.setWtime(LocalDateTime.now()); + return withdrawalAdjustment; + } + + public static FistfulCashFlow createFistfulCashFlow() { + FistfulCashFlow cashFlow = new FistfulCashFlow(); + cashFlow.setAmount(100L); + cashFlow.setCurrencyCode("RUB"); + cashFlow.setObjType(FistfulCashFlowChangeType.withdrawal_adjustment); + cashFlow.setDestinationAccountId("d_id"); + cashFlow.setDestinationAccountType(dev.vality.newway.domain.enums.CashFlowAccount.merchant); + cashFlow.setDestinationAccountTypeValue("type"); + cashFlow.setSourceAccountTypeValue("type"); + cashFlow.setSourceAccountType(dev.vality.newway.domain.enums.CashFlowAccount.wallet); + cashFlow.setSourceAccountId("s_id"); + return cashFlow; + } + } diff --git a/src/test/java/dev/vality/newway/config/PostgresqlJooqSpringBootITest.java b/src/test/java/dev/vality/newway/config/PostgresqlJooqSpringBootITest.java new file mode 100644 index 00000000..9036ed39 --- /dev/null +++ b/src/test/java/dev/vality/newway/config/PostgresqlJooqSpringBootITest.java @@ -0,0 +1,16 @@ +package dev.vality.newway.config; + +import dev.vality.testcontainers.annotations.postgresql.PostgresqlTestcontainerSingleton; +import org.springframework.boot.test.autoconfigure.jooq.JooqTest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@PostgresqlTestcontainerSingleton +@JooqTest +public @interface PostgresqlJooqSpringBootITest { +} diff --git a/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentCreatedHandlerTest.java b/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentCreatedHandlerTest.java new file mode 100644 index 00000000..f12a3347 --- /dev/null +++ b/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentCreatedHandlerTest.java @@ -0,0 +1,58 @@ +package dev.vality.newway.handler.event.stock.impl.withdrawal; + +import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.newway.TestData; +import dev.vality.newway.config.PostgresqlJooqSpringBootITest; +import dev.vality.newway.dao.withdrawal.impl.WithdrawalAdjustmentDaoImpl; +import dev.vality.newway.domain.tables.records.WithdrawalAdjustmentRecord; +import dev.vality.newway.factory.machine.event.WithdrawalAdjustmentMachineEventCopyFactoryImpl; +import org.jooq.DSLContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; + +import static dev.vality.newway.domain.tables.WithdrawalAdjustment.WITHDRAWAL_ADJUSTMENT; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +@PostgresqlJooqSpringBootITest +@ContextConfiguration(classes = {WithdrawalAdjustmentDaoImpl.class, WithdrawalAdjustmentCreatedHandler.class, + WithdrawalAdjustmentMachineEventCopyFactoryImpl.class,}) +class WithdrawalAdjustmentCreatedHandlerTest { + + @Autowired + WithdrawalAdjustmentCreatedHandler handler; + + @Autowired + DSLContext dslContext; + + @BeforeEach + void setUp() { + dslContext.deleteFrom(WITHDRAWAL_ADJUSTMENT).execute(); + } + + @Test + void handledStatusChange() { + TimestampedChange timestampedChange = TestData.createWithdrawalAdjustmentCreatedChange("adjustmentId"); + + handler.handle(timestampedChange, TestData.createWithdrawalAdjustmentdMachineEvent(timestampedChange)); + + WithdrawalAdjustmentRecord record = dslContext.fetchAny(WITHDRAWAL_ADJUSTMENT); + assertNotNull(record); + assertNotNull(record.getWithdrawalStatus()); + assertNull(record.getDomainRevision()); + } + + @Test + void handleDomainRevisionChange() { + TimestampedChange timestampedChange = TestData.createWithdrawalAdjustmentCreatedDomainRevisionChange("adjustmentId"); + + handler.handle(timestampedChange, TestData.createWithdrawalAdjustmentdMachineEvent(timestampedChange)); + + WithdrawalAdjustmentRecord record = dslContext.fetchAny(WITHDRAWAL_ADJUSTMENT); + assertNotNull(record); + assertNull(record.getWithdrawalStatus()); + assertNotNull(record.getDomainRevision()); + } +} diff --git a/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentStatusChangedHandlerTest.java b/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentStatusChangedHandlerTest.java new file mode 100644 index 00000000..ba1205d0 --- /dev/null +++ b/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentStatusChangedHandlerTest.java @@ -0,0 +1,58 @@ +package dev.vality.newway.handler.event.stock.impl.withdrawal; + +import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.TestData; +import dev.vality.newway.config.PostgresqlJooqSpringBootITest; +import dev.vality.newway.dao.withdrawal.impl.WithdrawalAdjustmentDaoImpl; +import dev.vality.newway.domain.enums.WithdrawalAdjustmentStatus; +import dev.vality.newway.domain.tables.pojos.WithdrawalAdjustment; +import dev.vality.newway.domain.tables.records.WithdrawalAdjustmentRecord; +import dev.vality.newway.factory.machine.event.WithdrawalAdjustmentMachineEventCopyFactoryImpl; +import org.jooq.DSLContext; +import org.jooq.Result; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; + +import static dev.vality.newway.domain.tables.WithdrawalAdjustment.WITHDRAWAL_ADJUSTMENT; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@PostgresqlJooqSpringBootITest +@ContextConfiguration(classes = {WithdrawalAdjustmentDaoImpl.class, WithdrawalAdjustmentStatusChangedHandler.class, + WithdrawalAdjustmentMachineEventCopyFactoryImpl.class,}) +class WithdrawalAdjustmentStatusChangedHandlerTest { + + @Autowired + WithdrawalAdjustmentStatusChangedHandler handler; + + @Autowired + DSLContext dslContext; + + @BeforeEach + void setUp() { + dslContext.deleteFrom(WITHDRAWAL_ADJUSTMENT).execute(); + } + + @Test + void handle() { + String adjustmentId = "adjustment_id"; + WithdrawalAdjustment withdrawalAdjustment = TestData.createWithdrawalAdjustment(adjustmentId); + dslContext.insertInto(WITHDRAWAL_ADJUSTMENT) + .set(dslContext.newRecord(WITHDRAWAL_ADJUSTMENT, withdrawalAdjustment)) + .execute(); + TimestampedChange timestampedChange = TestData.createWithdrawalAdjustmentStatusChange(adjustmentId); + MachineEvent event = TestData.createWithdrawalAdjustmentdMachineEvent(timestampedChange); + event.setSourceId(withdrawalAdjustment.getWithdrawalId()); + + handler.handle(timestampedChange, event); + + Result recordNew = dslContext.fetch(WITHDRAWAL_ADJUSTMENT, WITHDRAWAL_ADJUSTMENT.CURRENT.eq(Boolean.TRUE)); + assertEquals(1, recordNew.size()); + assertEquals(WithdrawalAdjustmentStatus.succeeded, recordNew.get(0).getStatus()); + Result recordOld = dslContext.fetch(WITHDRAWAL_ADJUSTMENT, WITHDRAWAL_ADJUSTMENT.CURRENT.eq(Boolean.FALSE)); + assertEquals(1, recordOld.size()); + assertEquals(WithdrawalAdjustmentStatus.pending, recordOld.get(0).getStatus()); + } +} diff --git a/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferCreatedHandlerTest.java b/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferCreatedHandlerTest.java new file mode 100644 index 00000000..210ba948 --- /dev/null +++ b/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferCreatedHandlerTest.java @@ -0,0 +1,64 @@ +package dev.vality.newway.handler.event.stock.impl.withdrawal; + +import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.TestData; +import dev.vality.newway.config.PostgresqlJooqSpringBootITest; +import dev.vality.newway.dao.withdrawal.impl.FistfulCashFlowDaoImpl; +import dev.vality.newway.dao.withdrawal.impl.WithdrawalAdjustmentDaoImpl; +import dev.vality.newway.domain.enums.WithdrawalTransferStatus; +import dev.vality.newway.domain.tables.pojos.WithdrawalAdjustment; +import dev.vality.newway.domain.tables.records.WithdrawalAdjustmentRecord; +import dev.vality.newway.factory.machine.event.WithdrawalAdjustmentMachineEventCopyFactoryImpl; +import org.jooq.DSLContext; +import org.jooq.Result; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; + +import static dev.vality.newway.domain.tables.FistfulCashFlow.FISTFUL_CASH_FLOW; +import static dev.vality.newway.domain.tables.WithdrawalAdjustment.WITHDRAWAL_ADJUSTMENT; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@PostgresqlJooqSpringBootITest +@ContextConfiguration(classes = {WithdrawalAdjustmentDaoImpl.class, FistfulCashFlowDaoImpl.class, + WithdrawalAdjustmentTransferCreatedHandler.class, + WithdrawalAdjustmentMachineEventCopyFactoryImpl.class,}) +class WithdrawalAdjustmentTransferCreatedHandlerTest { + + @Autowired + WithdrawalAdjustmentTransferCreatedHandler handler; + + @Autowired + DSLContext dslContext; + + @BeforeEach + void setUp() { + dslContext.deleteFrom(WITHDRAWAL_ADJUSTMENT).execute(); + dslContext.deleteFrom(FISTFUL_CASH_FLOW).execute(); + } + + @Test + void handle() { + String adjustmentId = "adjustment_id"; + WithdrawalAdjustment withdrawalAdjustment = TestData.createWithdrawalAdjustment(adjustmentId); + dslContext.insertInto(WITHDRAWAL_ADJUSTMENT) + .set(dslContext.newRecord(WITHDRAWAL_ADJUSTMENT, withdrawalAdjustment)) + .execute(); + TimestampedChange timestampedChange = TestData.createWithdrawalAdjustmentTransferCreatedChange(adjustmentId); + + MachineEvent event = TestData.createWithdrawalAdjustmentdMachineEvent(timestampedChange); + event.setSourceId(withdrawalAdjustment.getWithdrawalId()); + + handler.handle(timestampedChange, event); + + Result recordNew = dslContext.fetch(WITHDRAWAL_ADJUSTMENT, WITHDRAWAL_ADJUSTMENT.CURRENT.eq(Boolean.TRUE)); + assertEquals(1, recordNew.size()); + assertEquals(WithdrawalTransferStatus.created, recordNew.get(0).getWithdrawalTransferStatus()); + assertNotNull(recordNew.get(0).getFee()); + assertNotNull(recordNew.get(0).getProviderFee()); + assertEquals(4, dslContext.fetchCount(FISTFUL_CASH_FLOW)); + } +} \ No newline at end of file diff --git a/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandlerTest.java b/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandlerTest.java new file mode 100644 index 00000000..95cff9a8 --- /dev/null +++ b/src/test/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandlerTest.java @@ -0,0 +1,67 @@ +package dev.vality.newway.handler.event.stock.impl.withdrawal; + +import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.TestData; +import dev.vality.newway.config.PostgresqlJooqSpringBootITest; +import dev.vality.newway.dao.withdrawal.impl.FistfulCashFlowDaoImpl; +import dev.vality.newway.dao.withdrawal.impl.WithdrawalAdjustmentDaoImpl; +import dev.vality.newway.domain.enums.WithdrawalTransferStatus; +import dev.vality.newway.domain.tables.pojos.FistfulCashFlow; +import dev.vality.newway.domain.tables.pojos.WithdrawalAdjustment; +import dev.vality.newway.domain.tables.records.WithdrawalAdjustmentRecord; +import dev.vality.newway.factory.machine.event.WithdrawalAdjustmentMachineEventCopyFactoryImpl; +import org.jooq.DSLContext; +import org.jooq.Result; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; + +import static dev.vality.newway.domain.tables.FistfulCashFlow.FISTFUL_CASH_FLOW; +import static dev.vality.newway.domain.tables.WithdrawalAdjustment.WITHDRAWAL_ADJUSTMENT; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@PostgresqlJooqSpringBootITest +@ContextConfiguration(classes = {WithdrawalAdjustmentDaoImpl.class, FistfulCashFlowDaoImpl.class, + WithdrawalAdjustmentTransferStatusChangedHandler.class, + WithdrawalAdjustmentMachineEventCopyFactoryImpl.class,}) +class WithdrawalAdjustmentTransferStatusChangedHandlerTest { + + @Autowired + WithdrawalAdjustmentTransferStatusChangedHandler handler; + + @Autowired + DSLContext dslContext; + + @BeforeEach + void setUp() { + dslContext.deleteFrom(WITHDRAWAL_ADJUSTMENT).execute(); + dslContext.deleteFrom(FISTFUL_CASH_FLOW).execute(); + } + + @Test + void handle() { + String adjustmentId = "adjustment_id"; + WithdrawalAdjustment withdrawalAdjustment = TestData.createWithdrawalAdjustment(adjustmentId); + dslContext.insertInto(WITHDRAWAL_ADJUSTMENT) + .set(dslContext.newRecord(WITHDRAWAL_ADJUSTMENT, withdrawalAdjustment)) + .execute(); + WithdrawalAdjustmentRecord withdrawalAdjustmentRecord = dslContext.fetchAny(WITHDRAWAL_ADJUSTMENT); + FistfulCashFlow fistfulCashFlow = TestData.createFistfulCashFlow(); + fistfulCashFlow.setObjId(withdrawalAdjustmentRecord.getId()); + dslContext.insertInto(FISTFUL_CASH_FLOW) + .set(dslContext.newRecord(FISTFUL_CASH_FLOW, fistfulCashFlow)) + .execute(); + TimestampedChange timestampedChange = TestData.createWithdrawalAdjustmentTransferStatusChange(adjustmentId); + MachineEvent event = TestData.createWithdrawalAdjustmentdMachineEvent(timestampedChange); + event.setSourceId(withdrawalAdjustment.getWithdrawalId()); + + handler.handle(timestampedChange, event); + + Result recordNew = dslContext.fetch(WITHDRAWAL_ADJUSTMENT, WITHDRAWAL_ADJUSTMENT.CURRENT.eq(Boolean.TRUE)); + assertEquals(1, recordNew.size()); + assertEquals(WithdrawalTransferStatus.committed, recordNew.get(0).getWithdrawalTransferStatus()); + assertEquals(2, dslContext.fetchCount(FISTFUL_CASH_FLOW)); + } +} \ No newline at end of file diff --git a/src/test/java/dev/vality/newway/kafka/KafkaProducer.java b/src/test/java/dev/vality/newway/kafka/KafkaProducer.java index 505161cd..e2397660 100644 --- a/src/test/java/dev/vality/newway/kafka/KafkaProducer.java +++ b/src/test/java/dev/vality/newway/kafka/KafkaProducer.java @@ -38,4 +38,10 @@ private MachineEvent createMessage() { message.setData(data); return message; } + + public void sendMessage(String topic, MachineEvent message) { + SinkEvent sinkEvent = new SinkEvent(); + sinkEvent.setEvent(message); + testThriftKafkaProducer.send(topic, sinkEvent); + } } diff --git a/src/test/java/dev/vality/newway/kafka/WithdrawalAdjustmentKafkaListenerTest.java b/src/test/java/dev/vality/newway/kafka/WithdrawalAdjustmentKafkaListenerTest.java new file mode 100644 index 00000000..7a664063 --- /dev/null +++ b/src/test/java/dev/vality/newway/kafka/WithdrawalAdjustmentKafkaListenerTest.java @@ -0,0 +1,83 @@ +package dev.vality.newway.kafka; + +import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.kafka.common.serialization.ThriftSerializer; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.TestData; +import dev.vality.newway.config.KafkaPostgresqlSpringBootITest; +import dev.vality.newway.dao.withdrawal.iface.FistfulCashFlowDao; +import dev.vality.newway.dao.withdrawal.iface.WithdrawalAdjustmentDao; +import dev.vality.newway.domain.tables.pojos.WithdrawalAdjustment; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.mock.mockito.MockBean; + +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import static org.mockito.ArgumentMatchers.*; + +@KafkaPostgresqlSpringBootITest +class WithdrawalAdjustmentKafkaListenerTest { + + @Value("${kafka.topics.withdrawal.id}") + public String topic; + + @Autowired + private KafkaProducer kafkaProducer; + + @MockBean + private WithdrawalAdjustmentDao withdrawalAdjustmentDao; + + @MockBean + private FistfulCashFlowDao fistfulCashFlowDao; + + @BeforeEach + void setUp() { + Mockito.reset(withdrawalAdjustmentDao); + Mockito.reset(fistfulCashFlowDao); + } + + @Test + void listenWithdrawalAdjustmentCreatedChange() { + TimestampedChange timestampedChange = TestData.createWithdrawalAdjustmentCreatedChange("adjustmentId"); + MachineEvent message = new MachineEvent(); + message.setCreatedAt("2023-07-03T10:15:30Z"); + message.setEventId(1L); + message.setSourceNs("sourceNs"); + message.setSourceId("sourceId"); + message.setData(dev.vality.machinegun.msgpack.Value.bin(new ThriftSerializer<>().serialize("", timestampedChange))); + + kafkaProducer.sendMessage(topic, message); + + Mockito.verify(withdrawalAdjustmentDao, Mockito.timeout(TimeUnit.MINUTES.toMillis(1)).times(1)) + .save(any()); + } + + @Test + void listenWithdrawalAdjustmentStatusChange() { + String adjustmentId = "adjustmentId"; + TimestampedChange timestampedChange = TestData.createWithdrawalAdjustmentStatusChange(adjustmentId); + MachineEvent message = new MachineEvent(); + message.setCreatedAt("2023-07-03T10:15:30Z"); + message.setEventId(1L); + message.setSourceNs("sourceNs"); + message.setSourceId("sourceId"); + message.setData(dev.vality.machinegun.msgpack.Value.bin(new ThriftSerializer<>().serialize("", timestampedChange))); + WithdrawalAdjustment withdrawalAdjustment = TestData.createWithdrawalAdjustment(adjustmentId); + withdrawalAdjustment.setId(1L); + Mockito.when(withdrawalAdjustmentDao.getByIds(anyString(), anyString())).thenReturn(withdrawalAdjustment); + Mockito.when(withdrawalAdjustmentDao.save(any(WithdrawalAdjustment.class))).thenReturn(Optional.of(1L)); + Mockito.doNothing().when(withdrawalAdjustmentDao).updateNotCurrent(anyLong()); + + kafkaProducer.sendMessage(topic, message); + + Mockito.verify(withdrawalAdjustmentDao, Mockito.timeout(TimeUnit.MINUTES.toMillis(3)).times(1)) + .getByIds(anyString(), anyString()); + Mockito.verify(withdrawalAdjustmentDao, Mockito.times(1)) + .save(any()); + } +} diff --git a/src/test/java/dev/vality/newway/kafka/WithdrawalAdjustmentTransferKafkaListenerTest.java b/src/test/java/dev/vality/newway/kafka/WithdrawalAdjustmentTransferKafkaListenerTest.java new file mode 100644 index 00000000..06ddd5cc --- /dev/null +++ b/src/test/java/dev/vality/newway/kafka/WithdrawalAdjustmentTransferKafkaListenerTest.java @@ -0,0 +1,99 @@ +package dev.vality.newway.kafka; + +import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.kafka.common.serialization.ThriftSerializer; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.TestData; +import dev.vality.newway.config.KafkaPostgresqlSpringBootITest; +import dev.vality.newway.dao.withdrawal.iface.FistfulCashFlowDao; +import dev.vality.newway.dao.withdrawal.iface.WithdrawalAdjustmentDao; +import dev.vality.newway.domain.tables.pojos.WithdrawalAdjustment; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.mock.mockito.MockBean; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import static org.mockito.ArgumentMatchers.*; + +@KafkaPostgresqlSpringBootITest +class WithdrawalAdjustmentTransferKafkaListenerTest { + + @Value("${kafka.topics.withdrawal.id}") + public String topic; + + @Autowired + private KafkaProducer kafkaProducer; + + @MockBean + private WithdrawalAdjustmentDao withdrawalAdjustmentDao; + + @MockBean + private FistfulCashFlowDao fistfulCashFlowDao; + + @BeforeEach + void setUp() { + Mockito.reset(withdrawalAdjustmentDao); + Mockito.reset(fistfulCashFlowDao); + } + + @Test + void listenWithdrawalAdjustmentTransferCreatedChange() { + String adjustmentId = "adjustmentId"; + TimestampedChange timestampedChange = TestData.createWithdrawalAdjustmentTransferCreatedChange(adjustmentId); + MachineEvent message = new MachineEvent(); + message.setCreatedAt("2023-07-03T10:15:30Z"); + message.setEventId(1L); + message.setSourceNs("sourceNs"); + message.setSourceId("sourceId"); + message.setData(dev.vality.machinegun.msgpack.Value.bin(new ThriftSerializer<>().serialize("", timestampedChange))); + WithdrawalAdjustment withdrawalAdjustment = TestData.createWithdrawalAdjustment(adjustmentId); + withdrawalAdjustment.setId(1L); + Mockito.when(withdrawalAdjustmentDao.getByIds(anyString(), anyString())).thenReturn(withdrawalAdjustment); + Mockito.when(withdrawalAdjustmentDao.save(any(WithdrawalAdjustment.class))).thenReturn(Optional.of(1L)); + Mockito.doNothing().when(withdrawalAdjustmentDao).updateNotCurrent(anyLong()); + + kafkaProducer.sendMessage(topic, message); + + Mockito.verify(withdrawalAdjustmentDao, Mockito.timeout(TimeUnit.MINUTES.toMillis(2)).times(1)) + .getByIds(anyString(), anyString()); + Mockito.verify(withdrawalAdjustmentDao, Mockito.timeout(TimeUnit.MINUTES.toMillis(1)).times(1)) + .save(any()); + Mockito.verify(fistfulCashFlowDao, Mockito.timeout(TimeUnit.MINUTES.toMillis(1)).times(1)) + .save(anyList()); + } + + @Test + void listenWithdrawalAdjustmentTransferStatusChange() { + String adjustmentId = "adjustmentId"; + TimestampedChange timestampedChange = TestData.createWithdrawalAdjustmentTransferStatusChange(adjustmentId); + MachineEvent message = new MachineEvent(); + message.setCreatedAt("2023-07-03T10:15:30Z"); + message.setEventId(1L); + message.setSourceNs("sourceNs"); + message.setSourceId("sourceId"); + message.setData(dev.vality.machinegun.msgpack.Value.bin(new ThriftSerializer<>().serialize("", timestampedChange))); + WithdrawalAdjustment withdrawalAdjustment = TestData.createWithdrawalAdjustment(adjustmentId); + withdrawalAdjustment.setId(1L); + Mockito.when(withdrawalAdjustmentDao.getByIds(anyString(), anyString())).thenReturn(withdrawalAdjustment); + Mockito.when(withdrawalAdjustmentDao.save(any(WithdrawalAdjustment.class))).thenReturn(Optional.of(1L)); + Mockito.doNothing().when(withdrawalAdjustmentDao).updateNotCurrent(anyLong()); + Mockito.when(fistfulCashFlowDao.getByObjId(anyLong(), any())).thenReturn(List.of(TestData.createFistfulCashFlow())); + + kafkaProducer.sendMessage(topic, message); + + Mockito.verify(withdrawalAdjustmentDao, Mockito.timeout(TimeUnit.MINUTES.toMillis(2)).times(1)) + .getByIds(anyString(), anyString()); + Mockito.verify(withdrawalAdjustmentDao, Mockito.timeout(TimeUnit.MINUTES.toMillis(1)).times(1)) + .save(any()); + Mockito.verify(fistfulCashFlowDao, Mockito.timeout(TimeUnit.MINUTES.toMillis(1)).times(1)) + .getByObjId(anyLong(), any()); + Mockito.verify(fistfulCashFlowDao, Mockito.timeout(TimeUnit.MINUTES.toMillis(1)).times(1)) + .save(anyList()); + } +} diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml new file mode 100644 index 00000000..9875f1f7 --- /dev/null +++ b/src/test/resources/logback-test.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + From 946ce65481fd316491cda85228c5fc43f17fba84 Mon Sep 17 00:00:00 2001 From: Gregory <32060161+ggmaleva@users.noreply.github.com> Date: Fri, 18 Aug 2023 18:04:55 +0300 Subject: [PATCH 32/36] fix adjustment id (#102) Co-authored-by: ggmaleva --- .../WithdrawalAdjustmentTransferStatusChangedHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandler.java index 1d14d2f4..68d67b6e 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandler.java @@ -52,7 +52,7 @@ public void handle(TimestampedChange timestampedChange, MachineEvent event) { final WithdrawalAdjustment withdrawalAdjustmentOld = withdrawalAdjustmentDao.getByIds(withdrawalId, withdrawalAdjustmentId); WithdrawalAdjustment withdrawalAdjustmentNew = machineEventCopyFactory - .create(event, sequenceId, withdrawalId, withdrawalAdjustmentOld, timestampedChange.getOccuredAt()); + .create(event, sequenceId, withdrawalAdjustmentId, withdrawalAdjustmentOld, timestampedChange.getOccuredAt()); withdrawalAdjustmentNew.setWithdrawalTransferStatus(TBaseUtil.unionFieldToEnum(status, WithdrawalTransferStatus.class)); withdrawalAdjustmentDao.save(withdrawalAdjustmentNew).ifPresentOrElse( From f3d96b10773962252437fd01e730f5d584fa7478 Mon Sep 17 00:00:00 2001 From: Gregory <32060161+ggmaleva@users.noreply.github.com> Date: Mon, 18 Sep 2023 12:53:36 +0300 Subject: [PATCH 33/36] add withdrawal adjustment listener (#104) * add withdrawal adjustment listener * fix review * fix review (2) --------- Co-authored-by: ggmaleva --- .../dev/vality/newway/config/KafkaConfig.java | 13 +++ .../properties/KafkaConsumerProperties.java | 1 + .../WithdrawalAdjustmentCreatedHandler.java | 2 +- .../WithdrawalAdjustmentHandler.java | 4 + ...hdrawalAdjustmentStatusChangedHandler.java | 2 +- ...rawalAdjustmentTransferCreatedHandler.java | 2 +- ...djustmentTransferStatusChangedHandler.java | 2 +- .../WithdrawalAdjustmentKafkaListener.java | 35 ++++++++ .../service/WithdrawalAdjustmentService.java | 37 +++++++++ src/main/resources/application.yml | 5 ++ src/test/java/dev/vality/newway/TestData.java | 20 +++++ ...WithdrawalAdjustmentKafkaListenerTest.java | 30 +++---- ...WithdrawalKafkaListenerAdjustmentTest.java | 83 +++++++++++++++++++ ...lKafkaListenerAdjustmentTransferTest.java} | 2 +- 14 files changed, 213 insertions(+), 25 deletions(-) create mode 100644 src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentHandler.java create mode 100644 src/main/java/dev/vality/newway/listener/WithdrawalAdjustmentKafkaListener.java create mode 100644 src/main/java/dev/vality/newway/service/WithdrawalAdjustmentService.java create mode 100644 src/test/java/dev/vality/newway/kafka/WithdrawalKafkaListenerAdjustmentTest.java rename src/test/java/dev/vality/newway/kafka/{WithdrawalAdjustmentTransferKafkaListenerTest.java => WithdrawalKafkaListenerAdjustmentTransferTest.java} (98%) diff --git a/src/main/java/dev/vality/newway/config/KafkaConfig.java b/src/main/java/dev/vality/newway/config/KafkaConfig.java index df10d067..fa0d6b19 100644 --- a/src/main/java/dev/vality/newway/config/KafkaConfig.java +++ b/src/main/java/dev/vality/newway/config/KafkaConfig.java @@ -38,6 +38,9 @@ public class KafkaConfig { @Value("${kafka.topics.exrate.consumer.group-id}") private String exrateConsumerGroup; + @Value("${kafka.topics.withdrawal-adjustment.consumer.group-id}") + private String withdrawalAdjustmentConsumerGroup; + @Bean public Map consumerConfigs() { return createConsumerConfig(); @@ -98,6 +101,16 @@ public KafkaListenerContainerFactory> withdrawalAdjustmentContainerFactory() { + Map props = kafkaProperties.buildConsumerProperties(); + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, SinkEventDeserializer.class); + props.put(ConsumerConfig.GROUP_ID_CONFIG, withdrawalAdjustmentConsumerGroup); + ConsumerFactory consumerFactory = new DefaultKafkaConsumerFactory<>(props); + return createConcurrentFactory(consumerFactory, kafkaConsumerProperties.getWithdrawalAdjustmentConcurrency()); + } + @Bean public KafkaListenerContainerFactory> payoutContainerFactory() { DefaultKafkaConsumerFactory kafkaConsumerFactory = diff --git a/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java b/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java index b6536a6f..c35b0b64 100644 --- a/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java +++ b/src/main/java/dev/vality/newway/config/properties/KafkaConsumerProperties.java @@ -26,5 +26,6 @@ public class KafkaConsumerProperties { private int withdrawalSessionConcurrency; private int limitConfigConcurrency; private int exrateConcurrency; + private int withdrawalAdjustmentConcurrency; } diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentCreatedHandler.java index 0c3c67a3..d337491a 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentCreatedHandler.java @@ -27,7 +27,7 @@ @Slf4j @Component @RequiredArgsConstructor -public class WithdrawalAdjustmentCreatedHandler implements WithdrawalHandler { +public class WithdrawalAdjustmentCreatedHandler implements WithdrawalHandler, WithdrawalAdjustmentHandler { private final WithdrawalAdjustmentDao withdrawalAdjustmentDao; private final MachineEventCopyFactory machineEventCopyFactory; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentHandler.java new file mode 100644 index 00000000..cca02860 --- /dev/null +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentHandler.java @@ -0,0 +1,4 @@ +package dev.vality.newway.handler.event.stock.impl.withdrawal; + +public interface WithdrawalAdjustmentHandler { +} diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentStatusChangedHandler.java index 0bc101d5..f2046c63 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentStatusChangedHandler.java @@ -21,7 +21,7 @@ @Slf4j @Component @RequiredArgsConstructor -public class WithdrawalAdjustmentStatusChangedHandler implements WithdrawalHandler { +public class WithdrawalAdjustmentStatusChangedHandler implements WithdrawalHandler, WithdrawalAdjustmentHandler { private final WithdrawalAdjustmentDao withdrawalAdjustmentDao; private final MachineEventCopyFactory machineEventCopyFactory; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferCreatedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferCreatedHandler.java index f011ab82..0aa11390 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferCreatedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferCreatedHandler.java @@ -27,7 +27,7 @@ @Slf4j @Component @RequiredArgsConstructor -public class WithdrawalAdjustmentTransferCreatedHandler implements WithdrawalHandler { +public class WithdrawalAdjustmentTransferCreatedHandler implements WithdrawalHandler, WithdrawalAdjustmentHandler { private final WithdrawalAdjustmentDao withdrawalAdjustmentDao; private final FistfulCashFlowDao fistfulCashFlowDao; diff --git a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandler.java b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandler.java index 68d67b6e..3e213877 100644 --- a/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandler.java +++ b/src/main/java/dev/vality/newway/handler/event/stock/impl/withdrawal/WithdrawalAdjustmentTransferStatusChangedHandler.java @@ -27,7 +27,7 @@ @Slf4j @Component @RequiredArgsConstructor -public class WithdrawalAdjustmentTransferStatusChangedHandler implements WithdrawalHandler { +public class WithdrawalAdjustmentTransferStatusChangedHandler implements WithdrawalHandler, WithdrawalAdjustmentHandler { private final WithdrawalAdjustmentDao withdrawalAdjustmentDao; private final FistfulCashFlowDao fistfulCashFlowDao; diff --git a/src/main/java/dev/vality/newway/listener/WithdrawalAdjustmentKafkaListener.java b/src/main/java/dev/vality/newway/listener/WithdrawalAdjustmentKafkaListener.java new file mode 100644 index 00000000..310baa40 --- /dev/null +++ b/src/main/java/dev/vality/newway/listener/WithdrawalAdjustmentKafkaListener.java @@ -0,0 +1,35 @@ +package dev.vality.newway.listener; + +import dev.vality.kafka.common.util.LogUtil; +import dev.vality.machinegun.eventsink.SinkEvent; +import dev.vality.newway.service.WithdrawalAdjustmentService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.support.Acknowledgment; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@RequiredArgsConstructor +@Service +public class WithdrawalAdjustmentKafkaListener { + + private final WithdrawalAdjustmentService withdrawalAdjustmentService; + + @KafkaListener( + autoStartup = "${kafka.topics.withdrawal-adjustment.enabled}", + topics = "${kafka.topics.withdrawal-adjustment.id}", + containerFactory = "withdrawalAdjustmentContainerFactory") + public void handle(List> messages, Acknowledgment ack) { + log.info("withdrawalAdjustmentKafkaListener got machineEvent batch with size: {}", messages.size()); + withdrawalAdjustmentService.handleEvents(messages.stream() + .map(m -> m.value().getEvent()) + .toList()); + ack.acknowledge(); + log.info("withdrawalAdjustmentKafkaListener batch has been committed, size={}, {}", messages.size(), + LogUtil.toSummaryStringWithSinkEventValues(messages)); + } +} diff --git a/src/main/java/dev/vality/newway/service/WithdrawalAdjustmentService.java b/src/main/java/dev/vality/newway/service/WithdrawalAdjustmentService.java new file mode 100644 index 00000000..e21234e6 --- /dev/null +++ b/src/main/java/dev/vality/newway/service/WithdrawalAdjustmentService.java @@ -0,0 +1,37 @@ +package dev.vality.newway.service; + +import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.handler.event.stock.impl.withdrawal.WithdrawalAdjustmentHandler; +import dev.vality.newway.handler.event.stock.impl.withdrawal.WithdrawalHandler; +import dev.vality.sink.common.parser.impl.MachineEventParser; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + + +@Service +@RequiredArgsConstructor +public class WithdrawalAdjustmentService { + + private final MachineEventParser parser; + private final List handlers; + + @Transactional(propagation = Propagation.REQUIRED) + public void handleEvents(List machineEvents) { + machineEvents.forEach(this::handleIfAccept); + } + + private void handleIfAccept(MachineEvent machineEvent) { + TimestampedChange eventPayload = parser.parse(machineEvent); + if (eventPayload.isSetChange()) { + handlers.stream() + .map(WithdrawalHandler.class::cast) + .filter(handler -> handler.accept(eventPayload)) + .forEach(handler -> handler.handle(eventPayload, machineEvent)); + } + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 545201fc..426bc9af 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -68,6 +68,7 @@ kafka: withdrawal-session-concurrency: 7 limit-config-concurrency: 7 exrate-concurrency: 7 + withdrawal-adjustment-concurrency: 7 topics: invoice: id: mg-invoice-100-2 @@ -113,6 +114,10 @@ kafka: id: etl-exchange-rate enabled: false consumer.group-id: "daway-exrate" + withdrawal-adjustment: + id: mg-events-ff-withdrawal + enabled: false + consumer.group-id: "daway-withdrawal-adjustment" dmt: url: http://dominant:8022/v1/domain/repository diff --git a/src/test/java/dev/vality/newway/TestData.java b/src/test/java/dev/vality/newway/TestData.java index 09cf511e..3f99dca7 100644 --- a/src/test/java/dev/vality/newway/TestData.java +++ b/src/test/java/dev/vality/newway/TestData.java @@ -9,6 +9,7 @@ import dev.vality.fistful.withdrawal.AdjustmentChange; import dev.vality.fistful.withdrawal.Change; import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.fistful.withdrawal.Withdrawal; import dev.vality.fistful.withdrawal.adjustment.*; import dev.vality.geck.common.util.TypeUtil; import dev.vality.kafka.common.serialization.ThriftSerializer; @@ -487,4 +488,23 @@ public static FistfulCashFlow createFistfulCashFlow() { return cashFlow; } + public static TimestampedChange createWithdrawalCreatedChange(String id) { + Withdrawal withdrawal = new Withdrawal(); + withdrawal.setId(id); + withdrawal.setDestinationId(randomString()); + withdrawal.setWalletId(randomString()); + withdrawal.setBody(new dev.vality.fistful.base.Cash() + .setAmount(100L) + .setCurrency(new dev.vality.fistful.base.CurrencyRef() + .setSymbolicCode("RUB"))); + dev.vality.fistful.withdrawal.CreatedChange createdChange = new dev.vality.fistful.withdrawal.CreatedChange(); + createdChange.setWithdrawal(withdrawal); + Change change = new Change(); + change.setCreated(createdChange); + TimestampedChange timestampedChange = new TimestampedChange(); + timestampedChange.setOccuredAt("2023-07-03T10:15:30Z"); + timestampedChange.setChange(change); + return timestampedChange; + } + } diff --git a/src/test/java/dev/vality/newway/kafka/WithdrawalAdjustmentKafkaListenerTest.java b/src/test/java/dev/vality/newway/kafka/WithdrawalAdjustmentKafkaListenerTest.java index 7a664063..3bab0811 100644 --- a/src/test/java/dev/vality/newway/kafka/WithdrawalAdjustmentKafkaListenerTest.java +++ b/src/test/java/dev/vality/newway/kafka/WithdrawalAdjustmentKafkaListenerTest.java @@ -5,22 +5,22 @@ import dev.vality.machinegun.eventsink.MachineEvent; import dev.vality.newway.TestData; import dev.vality.newway.config.KafkaPostgresqlSpringBootITest; -import dev.vality.newway.dao.withdrawal.iface.FistfulCashFlowDao; import dev.vality.newway.dao.withdrawal.iface.WithdrawalAdjustmentDao; -import dev.vality.newway.domain.tables.pojos.WithdrawalAdjustment; +import dev.vality.newway.dao.withdrawal.iface.WithdrawalDao; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.TestPropertySource; -import java.util.Optional; import java.util.concurrent.TimeUnit; -import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.any; @KafkaPostgresqlSpringBootITest +@TestPropertySource(properties = {"kafka.topics.withdrawal-adjustment.enabled=true"}) class WithdrawalAdjustmentKafkaListenerTest { @Value("${kafka.topics.withdrawal.id}") @@ -33,12 +33,12 @@ class WithdrawalAdjustmentKafkaListenerTest { private WithdrawalAdjustmentDao withdrawalAdjustmentDao; @MockBean - private FistfulCashFlowDao fistfulCashFlowDao; + private WithdrawalDao withdrawalDao; @BeforeEach void setUp() { Mockito.reset(withdrawalAdjustmentDao); - Mockito.reset(fistfulCashFlowDao); + Mockito.reset(withdrawalDao); } @Test @@ -53,31 +53,21 @@ void listenWithdrawalAdjustmentCreatedChange() { kafkaProducer.sendMessage(topic, message); - Mockito.verify(withdrawalAdjustmentDao, Mockito.timeout(TimeUnit.MINUTES.toMillis(1)).times(1)) + Mockito.verify(withdrawalAdjustmentDao, Mockito.timeout(TimeUnit.MINUTES.toMillis(1)).atLeastOnce()) .save(any()); } @Test - void listenWithdrawalAdjustmentStatusChange() { - String adjustmentId = "adjustmentId"; - TimestampedChange timestampedChange = TestData.createWithdrawalAdjustmentStatusChange(adjustmentId); + void doNotListenWithdrawalChange() { + TimestampedChange timestampedChange = TestData.createWithdrawalCreatedChange("withdrawalId"); MachineEvent message = new MachineEvent(); message.setCreatedAt("2023-07-03T10:15:30Z"); message.setEventId(1L); message.setSourceNs("sourceNs"); message.setSourceId("sourceId"); message.setData(dev.vality.machinegun.msgpack.Value.bin(new ThriftSerializer<>().serialize("", timestampedChange))); - WithdrawalAdjustment withdrawalAdjustment = TestData.createWithdrawalAdjustment(adjustmentId); - withdrawalAdjustment.setId(1L); - Mockito.when(withdrawalAdjustmentDao.getByIds(anyString(), anyString())).thenReturn(withdrawalAdjustment); - Mockito.when(withdrawalAdjustmentDao.save(any(WithdrawalAdjustment.class))).thenReturn(Optional.of(1L)); - Mockito.doNothing().when(withdrawalAdjustmentDao).updateNotCurrent(anyLong()); kafkaProducer.sendMessage(topic, message); - - Mockito.verify(withdrawalAdjustmentDao, Mockito.timeout(TimeUnit.MINUTES.toMillis(3)).times(1)) - .getByIds(anyString(), anyString()); - Mockito.verify(withdrawalAdjustmentDao, Mockito.times(1)) - .save(any()); + Mockito.verify(withdrawalDao, Mockito.after(TimeUnit.MINUTES.toMillis(1)).only()).save(any()); } } diff --git a/src/test/java/dev/vality/newway/kafka/WithdrawalKafkaListenerAdjustmentTest.java b/src/test/java/dev/vality/newway/kafka/WithdrawalKafkaListenerAdjustmentTest.java new file mode 100644 index 00000000..3ee1f117 --- /dev/null +++ b/src/test/java/dev/vality/newway/kafka/WithdrawalKafkaListenerAdjustmentTest.java @@ -0,0 +1,83 @@ +package dev.vality.newway.kafka; + +import dev.vality.fistful.withdrawal.TimestampedChange; +import dev.vality.kafka.common.serialization.ThriftSerializer; +import dev.vality.machinegun.eventsink.MachineEvent; +import dev.vality.newway.TestData; +import dev.vality.newway.config.KafkaPostgresqlSpringBootITest; +import dev.vality.newway.dao.withdrawal.iface.FistfulCashFlowDao; +import dev.vality.newway.dao.withdrawal.iface.WithdrawalAdjustmentDao; +import dev.vality.newway.domain.tables.pojos.WithdrawalAdjustment; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.mock.mockito.MockBean; + +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import static org.mockito.ArgumentMatchers.*; + +@KafkaPostgresqlSpringBootITest +class WithdrawalKafkaListenerAdjustmentTest { + + @Value("${kafka.topics.withdrawal.id}") + public String topic; + + @Autowired + private KafkaProducer kafkaProducer; + + @MockBean + private WithdrawalAdjustmentDao withdrawalAdjustmentDao; + + @MockBean + private FistfulCashFlowDao fistfulCashFlowDao; + + @BeforeEach + void setUp() { + Mockito.reset(withdrawalAdjustmentDao); + Mockito.reset(fistfulCashFlowDao); + } + + @Test + void listenWithdrawalAdjustmentCreatedChange() { + TimestampedChange timestampedChange = TestData.createWithdrawalAdjustmentCreatedChange("adjustmentId"); + MachineEvent message = new MachineEvent(); + message.setCreatedAt("2023-07-03T10:15:30Z"); + message.setEventId(1L); + message.setSourceNs("sourceNs"); + message.setSourceId("sourceId"); + message.setData(dev.vality.machinegun.msgpack.Value.bin(new ThriftSerializer<>().serialize("", timestampedChange))); + + kafkaProducer.sendMessage(topic, message); + + Mockito.verify(withdrawalAdjustmentDao, Mockito.timeout(TimeUnit.MINUTES.toMillis(1)).times(1)) + .save(any()); + } + + @Test + void listenWithdrawalAdjustmentStatusChange() { + String adjustmentId = "adjustmentId"; + TimestampedChange timestampedChange = TestData.createWithdrawalAdjustmentStatusChange(adjustmentId); + MachineEvent message = new MachineEvent(); + message.setCreatedAt("2023-07-03T10:15:30Z"); + message.setEventId(1L); + message.setSourceNs("sourceNs"); + message.setSourceId("sourceId"); + message.setData(dev.vality.machinegun.msgpack.Value.bin(new ThriftSerializer<>().serialize("", timestampedChange))); + WithdrawalAdjustment withdrawalAdjustment = TestData.createWithdrawalAdjustment(adjustmentId); + withdrawalAdjustment.setId(1L); + Mockito.when(withdrawalAdjustmentDao.getByIds(anyString(), anyString())).thenReturn(withdrawalAdjustment); + Mockito.when(withdrawalAdjustmentDao.save(any(WithdrawalAdjustment.class))).thenReturn(Optional.of(1L)); + Mockito.doNothing().when(withdrawalAdjustmentDao).updateNotCurrent(anyLong()); + + kafkaProducer.sendMessage(topic, message); + + Mockito.verify(withdrawalAdjustmentDao, Mockito.timeout(TimeUnit.MINUTES.toMillis(3)).times(1)) + .getByIds(anyString(), anyString()); + Mockito.verify(withdrawalAdjustmentDao, Mockito.times(1)) + .save(any()); + } +} diff --git a/src/test/java/dev/vality/newway/kafka/WithdrawalAdjustmentTransferKafkaListenerTest.java b/src/test/java/dev/vality/newway/kafka/WithdrawalKafkaListenerAdjustmentTransferTest.java similarity index 98% rename from src/test/java/dev/vality/newway/kafka/WithdrawalAdjustmentTransferKafkaListenerTest.java rename to src/test/java/dev/vality/newway/kafka/WithdrawalKafkaListenerAdjustmentTransferTest.java index 06ddd5cc..88918688 100644 --- a/src/test/java/dev/vality/newway/kafka/WithdrawalAdjustmentTransferKafkaListenerTest.java +++ b/src/test/java/dev/vality/newway/kafka/WithdrawalKafkaListenerAdjustmentTransferTest.java @@ -22,7 +22,7 @@ import static org.mockito.ArgumentMatchers.*; @KafkaPostgresqlSpringBootITest -class WithdrawalAdjustmentTransferKafkaListenerTest { +class WithdrawalKafkaListenerAdjustmentTransferTest { @Value("${kafka.topics.withdrawal.id}") public String topic; From 43b53e6512cd014dfb12889a56f7d679743c4ba7 Mon Sep 17 00:00:00 2001 From: Gregory <32060161+ggmaleva@users.noreply.github.com> Date: Wed, 20 Sep 2023 18:08:50 +0300 Subject: [PATCH 34/36] add rack property in consumer group from file (#105) Co-authored-by: ggmaleva --- .../dev/vality/newway/config/KafkaConfig.java | 14 +++++++ .../vality/newway/service/FileService.java | 38 +++++++++++++++++++ src/main/resources/application.yml | 2 + .../newway/service/FileServiceTest.java | 37 ++++++++++++++++++ src/test/resources/rack.txt | 1 + 5 files changed, 92 insertions(+) create mode 100644 src/main/java/dev/vality/newway/service/FileService.java create mode 100644 src/test/java/dev/vality/newway/service/FileServiceTest.java create mode 100644 src/test/resources/rack.txt diff --git a/src/main/java/dev/vality/newway/config/KafkaConfig.java b/src/main/java/dev/vality/newway/config/KafkaConfig.java index fa0d6b19..6b455f19 100644 --- a/src/main/java/dev/vality/newway/config/KafkaConfig.java +++ b/src/main/java/dev/vality/newway/config/KafkaConfig.java @@ -7,6 +7,7 @@ import dev.vality.newway.serde.CurrencyExchangeRateEventDeserializer; import dev.vality.newway.serde.PayoutEventDeserializer; import dev.vality.newway.serde.SinkEventDeserializer; +import dev.vality.newway.service.FileService; import dev.vality.payout.manager.Event; import lombok.RequiredArgsConstructor; import org.apache.kafka.clients.consumer.ConsumerConfig; @@ -23,6 +24,7 @@ import org.springframework.kafka.listener.ContainerProperties; import java.util.Map; +import java.util.Objects; @Configuration @RequiredArgsConstructor @@ -31,6 +33,7 @@ public class KafkaConfig { private final KafkaProperties kafkaProperties; private final KafkaConsumerProperties kafkaConsumerProperties; + private final FileService fileService; @Value("${kafka.topics.party-management.consumer.group-id}") private String partyConsumerGroup; @@ -41,6 +44,9 @@ public class KafkaConfig { @Value("${kafka.topics.withdrawal-adjustment.consumer.group-id}") private String withdrawalAdjustmentConsumerGroup; + @Value("${kafka.rack.path}") + private String rackPath; + @Bean public Map consumerConfigs() { return createConsumerConfig(); @@ -51,6 +57,10 @@ private Map createConsumerConfig() { props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, SinkEventDeserializer.class); props.put(ConsumerConfig.GROUP_ID_CONFIG, kafkaConsumerProperties.getGroupId()); + String clientRack = fileService.getClientRack(rackPath); + if (Objects.nonNull(clientRack)) { + props.put(ConsumerConfig.CLIENT_RACK_CONFIG, clientRack); + } return props; } @@ -107,6 +117,10 @@ public KafkaListenerContainerFactory consumerFactory = new DefaultKafkaConsumerFactory<>(props); return createConcurrentFactory(consumerFactory, kafkaConsumerProperties.getWithdrawalAdjustmentConcurrency()); } diff --git a/src/main/java/dev/vality/newway/service/FileService.java b/src/main/java/dev/vality/newway/service/FileService.java new file mode 100644 index 00000000..b20e02b6 --- /dev/null +++ b/src/main/java/dev/vality/newway/service/FileService.java @@ -0,0 +1,38 @@ +package dev.vality.newway.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Objects; + +@Slf4j +@Service +public class FileService { + + public static final String DELIMITER = "="; + + public String getClientRack(String path) { + if (Objects.isNull(path)) { + return null; + } + String rack = getProperty(path); + log.debug("Get kafka rack: {} for consumer group", rack); + return rack; + } + + private String getProperty(String stringFilePath) { + try { + var path = Paths.get(stringFilePath); + String content = Files.readString(path); + return content.split(DELIMITER)[1]; + } catch (IOException e) { + log.error("Can't parse property from path: {}", stringFilePath, e); + return null; + } + } + + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 426bc9af..7acdc993 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -52,6 +52,8 @@ management: include: health,info,prometheus kafka: + rack: + path: 'src/test/resources/rack.txt' consumer: group-id: "DawayListener" invoicing-concurrency: 7 diff --git a/src/test/java/dev/vality/newway/service/FileServiceTest.java b/src/test/java/dev/vality/newway/service/FileServiceTest.java new file mode 100644 index 00000000..8db6dd43 --- /dev/null +++ b/src/test/java/dev/vality/newway/service/FileServiceTest.java @@ -0,0 +1,37 @@ +package dev.vality.newway.service; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.junit.jupiter.api.Assertions.*; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = {FileService.class}) +class FileServiceTest { + + @Autowired + FileService fileService; + + @Test + void testFailGetClientRack() { + String path = "/fail/path"; + + String clientRack = fileService.getClientRack(path); + + assertNull(clientRack); + + } + + @Test + void testGetClientRack() { + String path = "src/test/resources/rack.txt"; + String clientRack = fileService.getClientRack(path); + + assertNotNull(clientRack); + assertEquals("euc1-az1", clientRack); + + } +} \ No newline at end of file diff --git a/src/test/resources/rack.txt b/src/test/resources/rack.txt new file mode 100644 index 00000000..39843f3e --- /dev/null +++ b/src/test/resources/rack.txt @@ -0,0 +1 @@ +CLIENT_RACK=euc1-az1 \ No newline at end of file From bc2a58d56d70fae2127f936463088ed4ac4df275 Mon Sep 17 00:00:00 2001 From: Gregory <32060161+ggmaleva@users.noreply.github.com> Date: Thu, 21 Sep 2023 16:42:24 +0300 Subject: [PATCH 35/36] change WithdrawalAdjustment constraint and index (#106) Co-authored-by: ggmaleva --- .../dao/withdrawal/impl/WithdrawalAdjustmentDaoImpl.java | 2 +- .../db/migration/V15__change_withdrawal_adjustment_idx.sql | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/db/migration/V15__change_withdrawal_adjustment_idx.sql diff --git a/src/main/java/dev/vality/newway/dao/withdrawal/impl/WithdrawalAdjustmentDaoImpl.java b/src/main/java/dev/vality/newway/dao/withdrawal/impl/WithdrawalAdjustmentDaoImpl.java index 7338a65d..210f5160 100644 --- a/src/main/java/dev/vality/newway/dao/withdrawal/impl/WithdrawalAdjustmentDaoImpl.java +++ b/src/main/java/dev/vality/newway/dao/withdrawal/impl/WithdrawalAdjustmentDaoImpl.java @@ -35,7 +35,7 @@ public Optional save(WithdrawalAdjustment withdrawalAdjustment) throws Dao Query query = getDslContext() .insertInto(WITHDRAWAL_ADJUSTMENT) .set(adjustmentRecord) - .onConflict(WITHDRAWAL_ADJUSTMENT.ADJUSTMENT_ID, WITHDRAWAL_ADJUSTMENT.SEQUENCE_ID) + .onConflict(WITHDRAWAL_ADJUSTMENT.ADJUSTMENT_ID, WITHDRAWAL_ADJUSTMENT.WITHDRAWAL_ID, WITHDRAWAL_ADJUSTMENT.SEQUENCE_ID) .doNothing() .returning(WITHDRAWAL_ADJUSTMENT.ID); diff --git a/src/main/resources/db/migration/V15__change_withdrawal_adjustment_idx.sql b/src/main/resources/db/migration/V15__change_withdrawal_adjustment_idx.sql new file mode 100644 index 00000000..65950829 --- /dev/null +++ b/src/main/resources/db/migration/V15__change_withdrawal_adjustment_idx.sql @@ -0,0 +1,7 @@ +DROP INDEX IF EXISTS withdrawal_adjustment_id_idx; +CREATE INDEX IF NOT EXISTS withdrawal_adjustment_idx ON dw.withdrawal_adjustment USING btree (adjustment_id, withdrawal_id); + +ALTER TABLE dw.withdrawal_adjustment DROP CONSTRAINT IF EXISTS withdrawal_adjustment_uniq; +ALTER TABLE dw.withdrawal_adjustment + ADD CONSTRAINT withdrawal_adjustment_uniq UNIQUE (adjustment_id, withdrawal_id, sequence_id); + From 420b93fddc514b4b3e10bffccb5227e77e5557cd Mon Sep 17 00:00:00 2001 From: Gregory <32060161+ggmaleva@users.noreply.github.com> Date: Fri, 22 Sep 2023 14:14:43 +0300 Subject: [PATCH 36/36] refactoring rack file flow (#107) Co-authored-by: ggmaleva --- src/main/java/dev/vality/newway/config/KafkaConfig.java | 2 +- src/main/java/dev/vality/newway/service/FileService.java | 2 +- src/main/resources/application.yml | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/dev/vality/newway/config/KafkaConfig.java b/src/main/java/dev/vality/newway/config/KafkaConfig.java index 6b455f19..45717fb1 100644 --- a/src/main/java/dev/vality/newway/config/KafkaConfig.java +++ b/src/main/java/dev/vality/newway/config/KafkaConfig.java @@ -44,7 +44,7 @@ public class KafkaConfig { @Value("${kafka.topics.withdrawal-adjustment.consumer.group-id}") private String withdrawalAdjustmentConsumerGroup; - @Value("${kafka.rack.path}") + @Value("${kafka.rack.path:/tmp/.kafka_rack_env}") private String rackPath; @Bean diff --git a/src/main/java/dev/vality/newway/service/FileService.java b/src/main/java/dev/vality/newway/service/FileService.java index b20e02b6..f0a9cc02 100644 --- a/src/main/java/dev/vality/newway/service/FileService.java +++ b/src/main/java/dev/vality/newway/service/FileService.java @@ -29,7 +29,7 @@ private String getProperty(String stringFilePath) { String content = Files.readString(path); return content.split(DELIMITER)[1]; } catch (IOException e) { - log.error("Can't parse property from path: {}", stringFilePath, e); + log.debug("Can't parse property from path: {}", stringFilePath, e); return null; } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 7acdc993..426bc9af 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -52,8 +52,6 @@ management: include: health,info,prometheus kafka: - rack: - path: 'src/test/resources/rack.txt' consumer: group-id: "DawayListener" invoicing-concurrency: 7