1

我目前在一个项目中工作,该项目使用带有 B2C 加速器的 Spartacus 3.2.2 和 SAP Commerce 2011。该项目不遵循传统的购物网站,而是利用 Spartacus 和 SAP Commerce 的功能来实现自己的目的。我们有一个单页访客结帐流程,其中在加载 Angular 组件时使用购物车设置送货地址、付款详细信息和送货模式。

上下文是假设用户有一个表单,他们在其中输入一些信息,如电子邮件和其他一些详细信息,然后单击下一步。将生成一个购物车,其中添加了一个示例产品并关联了电子邮件。用户被重定向到一个页面以查看他们想要提交的信息,该页面是单页访客结帐。当此页面加载时,付款详情、送货地址和送货方式在后台设置,用户不会注意到任何事情。他们点击提交并在后台创建订单,当他们登陆确认页面时,前端将显示其他用户。

问题是当用户在查看信息后提交信息时,订单调用失败并且我们得到“未设置交付模式”并且它是不一致的。

这是我们用于单页访客结帐的示例代码:

@Component({
  selector: "review-details",
  templateUrl: "./review-details.component.html",
  host: { class: "template-component" },
})
export class ReviewDetailsComponent implements OnInit, OnDestroy, AfterViewInit {

hasSetAddress: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
hasSetDeliveryMode: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
hasSetPaymentDetails: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

subscriptions:any = [];

ngOnInit() {

  // Just an example address. A proper valid dummy address and region is used for our project. 
  let address: Address = {
      firstName: "No Name",
      lastName: "No Name",
      email: "NoName@noname.com",
      line1: "XYZ Mason Street",
      line2: "",
      town: "yolotown",
      postalCode: "12345",
      country: { isocode: "US" },
      defaultAddress: false,
      region: {
        countryIso: "US",
        isocode: "US-CA",
        isocodeShort: "CA",
        name: "California",
      },
      titleCode: "",
    };
    let paymentDetails: PaymentDetails = {
      cardNumber: "4111111111111111",
      expiryMonth: "12",
      expiryYear: "2025",
      billingAddress: address,
      cvn: "123",
      accountHolderName: "No Name",
      cardType: {
        code: "visa",
        name: "Visa",
      },
      defaultPayment: false,
      saved: false,
    };
  }

  this.subscriptions = this.subscriptions.concat(
    {
        key: 'setAddress',
        value: this.checkoutDeliveryService.getDeliveryAddress().subscribe((addr: Address) => {
          if (_isNil(addr) || (!_isNil(addr) && _isEmpty(addr))) {
            this.checkoutDeliveryService.createAndSetAddress(address);
          } else {
            this.hasSetAddress.next(true);
          }

        })
      },
      {
        key: 'setDeliveryModeStatus',
        value: this.checkoutDeliveryService.getSupportedDeliveryModes().pipe(
          withLatestFrom(
            this.checkoutDeliveryService
              .getSelectedDeliveryMode()
              .pipe(map((deliveryMode: DeliveryMode) => deliveryMode && deliveryMode.code ? deliveryMode.code : null))
          )
        ).subscribe(([deliveryModes, code]: [DeliveryMode[], string]) => {
          if (_isNil(code)) {
            if (!_isNil(deliveryModes) && !_isEmpty(deliveryModes)) {
              code = this.checkoutConfigService.getPreferredDeliveryMode(deliveryModes);
              if (code) {
                this.checkoutDeliveryService.setDeliveryMode(code);
              }
            }
          } else {
            
            this.hasSetDeliveryMode.next(true);
          }
        })
      },
      {
        key: 'setPaymentDetails',
        value: this.checkoutPaymentService.getPaymentDetails().subscribe((paymentInfo: PaymentDetails) => {
          if (_isNil(paymentInfo) || (!_isNil(paymentInfo) && _isEmpty(paymentInfo))) {
            this.checkoutPaymentService.createPaymentDetails(paymentDetails);
          } else {
            this.checkoutPaymentService.paymentProcessSuccess();
            this.hasSetPaymentDetails.next(true);
          }
        })
      },
      {
        key: 'placeOrder',
        value: this.checkoutService
          .getOrderDetails()
          .pipe(filter(order => Object.keys(order).length !== 0))
          .subscribe(() => {
            this.routingService.go({ cxRoute: 'orderConfirmation' });
          })
      }
  )

}

所以现在的问题是:

  • 当它不一致时,我们甚至如何找到这种“未设置交付模式”的根本原因?为了提供更多上下文,对付款、地址和交付模式的请求调用正确,但是当最终购物车对象在请求完成后返回时,我们有时没有设置交付成本。

  • 有没有办法优化代码以排除可能发生的任何竞争条件?考虑到所有添加付款明细、地址和送货方式的操作都是为同一个购物车异步设置的。

任何帮助表示赞赏,甚至欢迎优化想法。

谢谢

参考:

4

2 回答 2

1

有关使用 OCC API 调用对结帐信息进行多次更新的说明,请参阅此 SAP 说明:

https://launchpad.support.sap.com/#/notes/2998330

摘要:要同时执行各种更新以进行结帐,您需要使用单个 API 调用。SAP 说明提供了可用于此方案的示例扩展。

最好的问候,杰瑞

于 2021-09-09T07:55:53.340 回答
0

这是我自己的问题的答案,感谢 SAP 支持和 Spartacus 开发团队帮助我解决这个问题。

除了 Jerry 的答案,它更像是一个永久且直接的解决方案,需要在前端和后端完成,这里是另一种解决方案,只需要我们只在前端进行更改。

可以通过以下方式更改代码,以使调用顺序加载购物车上的详细信息。

@Component({
  selector: "review-details",
  templateUrl: "./review-details.component.html",
  host: { class: "template-component" },
})
export class ReviewDetailsComponent implements OnInit, OnDestroy, AfterViewInit {

  hasSetAddress: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
  hasSetDeliveryMode: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
  hasSetPaymentDetails: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

  subscriptions: any = [];

  // Just an example address. A proper valid dummy address and region is used for our project.
  private address: Address = {
    firstName: "No Name",
    lastName: "No Name",
    email: "NoName@noname.com",
    line1: "XYZ Mason Street",
    line2: "",
    town: "yolotown",
    postalCode: "12345",
    country: { isocode: "US" },
    defaultAddress: false,
    region: {
      countryIso: "US",
      isocode: "US-CA",
      isocodeShort: "CA",
      name: "California",
    },
    titleCode: "",
  }
  private paymentDetails: PaymentDetails = {
    cardNumber: "4111111111111111",
    expiryMonth: "12",
    expiryYear: "2025",
    billingAddress: address,
    cvn: "123",
    accountHolderName: "No Name",
    cardType: {
      code: "visa",
      name: "Visa",
    },
    defaultPayment: false,
    saved: false,
  }

  private address$: Observable<Address> = this.checkoutDeliveryService.getDeliveryAddress().pipe(
    tap((addr: Address) => {
      if (_isNil(addr) || (!_isNil(addr) && _isEmpty(addr))) {
        this.checkoutDeliveryService.createAndSetAddress(this.address);
      }
    }),
    filter((addr) => (!_isNil(addr) && !_isEmpty(addr)))
  );

  private deliveryMode$: Observable<string> = this.checkoutDeliveryService.getSupportedDeliveryModes().pipe(
    filter((deliveryModes: DeliveryMode[]) => deliveryModes?.length !== 0),
    withLatestFrom(
      this.checkoutDeliveryService.getSelectedDeliveryMode()
        .pipe(map((selectedDeliveryMode: DeliveryMode) => selectedDeliveryMode?.code))
    ),
    map(([deliveryModes, code]: [DeliveryMode[], string]) => {
      return code
        ? code
        : this.checkoutConfigService.getPreferredDeliveryMode(deliveryModes);
    }),
    tap((code) => {
      if (code) {
        this.checkoutDeliveryService.setDeliveryMode(code);
      }
    }),
    filter((code) => !!code)
  );

  private payment$: Observable<PaymentDetails> = this.checkoutPaymentService.getPaymentDetails().pipe(
    tap((paymentInfo: PaymentDetails) => {
      if (_isNil(paymentInfo) || (!_isNil(paymentInfo) && _isEmpty(paymentInfo))) {
        this.checkoutPaymentService.createPaymentDetails(this.paymentDetails);
      }
    }),
    filter((paymentInfo) => (!_isNil(paymentInfo) && !_isEmpty(paymentInfo)))
  );

  private setCartDetails$: Observable<boolean> = this.address$.pipe(
    take(1),
    map((_) => this.deliveryMode$.pipe(take(1))),
    concatAll(),
    map((_) => this.payment$.pipe(take(1))),
    concatAll(),
    map((_) => {
      this.checkoutPaymentService.paymentProcessSuccess();
      this.hasSetAddress.next(true);
      this.hasSetDeliveryMode.next(true);
      this.hasSetPaymentDetails.next(true);
      return true;
    }));

  ngOnInit() {

    this.subscriptions = this.subscriptions.concat(
      {
        key: "setCartDetails",
        value: this.setCartDetails$.subscribe((status: boolean) => console.log(status))
      },
      {
        key: 'placeOrder',
        value: this.checkoutService
          .getOrderDetails()
          .pipe(filter(order => Object.keys(order).length !== 0))
          .subscribe(() => {
            this.routingService.go({ cxRoute: 'orderConfirmation' });
          })
      }
    )
  }
}
于 2021-09-10T18:41:48.027 回答