Просмотр исходного кода

feat(admin-ui): Enable manual order state transitions

Michael Bromley 5 лет назад
Родитель
Сommit
0868b4c7f2
21 измененных файлов с 293 добавлено и 77 удалено
  1. 15 15
      packages/admin-ui/i18n-coverage.json
  2. 7 8
      packages/admin-ui/src/lib/core/src/common/generated-types.ts
  3. 14 2
      packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.html
  4. 21 10
      packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.ts
  5. 0 29
      packages/admin-ui/src/lib/order/src/components/order-detail/transition-to-pre-modifying-state.ts
  6. 2 2
      packages/admin-ui/src/lib/order/src/components/order-editor/order-editor.component.html
  7. 20 11
      packages/admin-ui/src/lib/order/src/components/order-editor/order-editor.component.ts
  8. 17 0
      packages/admin-ui/src/lib/order/src/components/order-state-select-dialog/order-state-select-dialog.component.html
  9. 0 0
      packages/admin-ui/src/lib/order/src/components/order-state-select-dialog/order-state-select-dialog.component.scss
  10. 26 0
      packages/admin-ui/src/lib/order/src/components/order-state-select-dialog/order-state-select-dialog.component.ts
  11. 2 0
      packages/admin-ui/src/lib/order/src/order.module.ts
  12. 133 0
      packages/admin-ui/src/lib/order/src/providers/order-transition.service.ts
  13. 4 0
      packages/admin-ui/src/lib/static/i18n-messages/cs.json
  14. 4 0
      packages/admin-ui/src/lib/static/i18n-messages/de.json
  15. 4 0
      packages/admin-ui/src/lib/static/i18n-messages/en.json
  16. 4 0
      packages/admin-ui/src/lib/static/i18n-messages/es.json
  17. 4 0
      packages/admin-ui/src/lib/static/i18n-messages/fr.json
  18. 4 0
      packages/admin-ui/src/lib/static/i18n-messages/pl.json
  19. 4 0
      packages/admin-ui/src/lib/static/i18n-messages/pt_BR.json
  20. 4 0
      packages/admin-ui/src/lib/static/i18n-messages/zh_Hans.json
  21. 4 0
      packages/admin-ui/src/lib/static/i18n-messages/zh_Hant.json

+ 15 - 15
packages/admin-ui/i18n-coverage.json

@@ -1,51 +1,51 @@
 {
-  "generatedOn": "2020-12-21T15:26:51.662Z",
-  "lastCommit": "3fda1021d033a38b878a6c6d8b5543bf0d554155",
+  "generatedOn": "2020-12-23T08:54:34.482Z",
+  "lastCommit": "e89845ec779938a7462fd15a103c1110afbf53af",
   "translationStatus": {
     "cs": {
-      "tokenCount": 743,
+      "tokenCount": 747,
       "translatedCount": 687,
       "percentage": 92
     },
     "de": {
-      "tokenCount": 743,
+      "tokenCount": 747,
       "translatedCount": 596,
       "percentage": 80
     },
     "en": {
-      "tokenCount": 743,
-      "translatedCount": 739,
-      "percentage": 99
+      "tokenCount": 747,
+      "translatedCount": 745,
+      "percentage": 100
     },
     "es": {
-      "tokenCount": 743,
+      "tokenCount": 747,
       "translatedCount": 455,
       "percentage": 61
     },
     "fr": {
-      "tokenCount": 743,
+      "tokenCount": 747,
       "translatedCount": 692,
       "percentage": 93
     },
     "pl": {
-      "tokenCount": 743,
+      "tokenCount": 747,
       "translatedCount": 551,
       "percentage": 74
     },
     "pt_BR": {
-      "tokenCount": 743,
+      "tokenCount": 747,
       "translatedCount": 642,
       "percentage": 86
     },
     "zh_Hans": {
-      "tokenCount": 743,
+      "tokenCount": 747,
       "translatedCount": 533,
-      "percentage": 72
+      "percentage": 71
     },
     "zh_Hant": {
-      "tokenCount": 743,
+      "tokenCount": 747,
       "translatedCount": 533,
-      "percentage": 72
+      "percentage": 71
     }
   }
 }

+ 7 - 8
packages/admin-ui/src/lib/core/src/common/generated-types.ts

@@ -1598,7 +1598,7 @@ export type ModifyOrderInput = {
   dryRun: Scalars['Boolean'];
   orderId: Scalars['ID'];
   addItems?: Maybe<Array<AddItemInput>>;
-  adjustOrderLines?: Maybe<Array<OrderLineInput>>;
+  adjustOrderLines?: Maybe<Array<AdjustOrderLineInput>>;
   surcharges?: Maybe<Array<SurchargeInput>>;
   updateShippingAddress?: Maybe<UpdateOrderAddressInput>;
   updateBillingAddress?: Maybe<UpdateOrderAddressInput>;
@@ -1612,6 +1612,11 @@ export type AddItemInput = {
   quantity: Scalars['Int'];
 };
 
+export type AdjustOrderLineInput = {
+  orderLineId: Scalars['ID'];
+  quantity: Scalars['Int'];
+};
+
 export type SurchargeInput = {
   description: Scalars['String'];
   sku?: Maybe<Scalars['String']>;
@@ -3834,7 +3839,7 @@ export type OrderLine = Node & {
   discounts: Array<Adjustment>;
   taxLines: Array<TaxLine>;
   order: Order;
-  customFields?: Maybe<OrderLineCustomFields>;
+  customFields?: Maybe<Scalars['JSON']>;
 };
 
 export type Payment = Node & {
@@ -4579,12 +4584,6 @@ export type HistoryEntrySortParameter = {
   updatedAt?: Maybe<SortOrder>;
 };
 
-export type OrderLineCustomFields = {
-  __typename?: 'OrderLineCustomFields';
-  test?: Maybe<Scalars['String']>;
-  test2?: Maybe<Scalars['String']>;
-};
-
 export type AuthenticationInput = {
   native?: Maybe<NativeAuthInput>;
 };

+ 14 - 2
packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.html

@@ -54,9 +54,16 @@
                         {{ 'order.cancel-order' | translate }}
                     </ng-template>
                 </button>
-                <ng-container *ngFor="let nextState of nextStates$ | async">
+
+                <ng-container *ngIf="(nextStates$ | async)?.length">
                     <div class="dropdown-divider"></div>
-                    <button type="button" class="btn" vdrDropdownItem (click)="transitionToState(nextState)">
+                    <button
+                        *ngFor="let nextState of nextStates$ | async"
+                        type="button"
+                        class="btn"
+                        vdrDropdownItem
+                        (click)="transitionToState(nextState)"
+                    >
                         <clr-icon shape="step-forward-2"></clr-icon>
                         {{
                             'order.transition-to-state'
@@ -64,6 +71,11 @@
                         }}
                     </button>
                 </ng-container>
+                <div class="dropdown-divider"></div>
+                <button type="button" class="btn" vdrDropdownItem (click)="manuallyTransitionToState(order)">
+                    <clr-icon shape="step-forward-2" class="is-warning"></clr-icon>
+                    {{ 'order.manually-transition-to-state' | translate }}
+                </button>
             </vdr-dropdown-menu>
         </vdr-dropdown>
     </vdr-ab-right>

+ 21 - 10
packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.ts

@@ -26,6 +26,7 @@ import { summate } from '@vendure/common/lib/shared-utils';
 import { EMPTY, merge, Observable, of, Subject } from 'rxjs';
 import { map, mapTo, startWith, switchMap, take } from 'rxjs/operators';
 
+import { OrderTransitionService } from '../../providers/order-transition.service';
 import { AddManualPaymentDialogComponent } from '../add-manual-payment-dialog/add-manual-payment-dialog.component';
 import { CancelOrderDialogComponent } from '../cancel-order-dialog/cancel-order-dialog.component';
 import { FulfillOrderDialogComponent } from '../fulfill-order-dialog/fulfill-order-dialog.component';
@@ -33,8 +34,6 @@ import { OrderProcessGraphDialogComponent } from '../order-process-graph-dialog/
 import { RefundOrderDialogComponent } from '../refund-order-dialog/refund-order-dialog.component';
 import { SettleRefundDialogComponent } from '../settle-refund-dialog/settle-refund-dialog.component';
 
-import { transitionToPreModifyingState } from './transition-to-pre-modifying-state';
-
 @Component({
     selector: 'vdr-order-detail',
     templateUrl: './order-detail.component.html',
@@ -71,6 +70,7 @@ export class OrderDetailComponent
         protected dataService: DataService,
         private notificationService: NotificationService,
         private modalService: ModalService,
+        private orderTransitionService: OrderTransitionService,
     ) {
         super(route, router, serverConfigService, dataService);
     }
@@ -139,6 +139,18 @@ export class OrderDetailComponent
         });
     }
 
+    manuallyTransitionToState(order: OrderDetailFragment) {
+        this.orderTransitionService
+            .manuallyTransitionToState({
+                orderId: order.id,
+                nextStates: order.nextStates,
+                cancellable: true,
+                message: _('order.manually-transition-to-state-message'),
+                retry: 0,
+            })
+            .subscribe();
+    }
+
     transitionToModifying() {
         this.dataService.order
             .transitionToState(this.id, 'Modifying')
@@ -243,7 +255,10 @@ export class OrderDetailComponent
                     switch (addManualPaymentToOrder.__typename) {
                         case 'Order':
                             this.notificationService.success('order.add-payment-to-order-success');
-                            return transitionToPreModifyingState(this.dataService, order.id);
+                            return this.orderTransitionService.transitionToPreModifyingState(
+                                order.id,
+                                order.nextStates,
+                            );
                         case 'ManualPaymentStateError':
                             this.notificationService.error(addManualPaymentToOrder.message);
                             return EMPTY;
@@ -252,13 +267,9 @@ export class OrderDetailComponent
                     }
                 }),
             )
-            .subscribe(({ transitionOrderToState }) => {
-                switch (transitionOrderToState?.__typename) {
-                    case 'Order':
-                        this.refetchOrder(transitionOrderToState);
-                        break;
-                    case 'OrderStateTransitionError':
-                        this.notificationService.error(transitionOrderToState.message);
+            .subscribe(result => {
+                if (result) {
+                    this.refetchOrder({ result });
                 }
             });
     }

+ 0 - 29
packages/admin-ui/src/lib/order/src/components/order-detail/transition-to-pre-modifying-state.ts

@@ -1,29 +0,0 @@
-import { DataService, HistoryEntryType, SortOrder } from '@vendure/admin-ui/core';
-import { EMPTY } from 'rxjs';
-import { switchMap } from 'rxjs/operators';
-
-export function transitionToPreModifyingState(dataService: DataService, orderId: string) {
-    return dataService.order
-        .getOrderHistory(orderId, {
-            filter: {
-                type: {
-                    eq: HistoryEntryType.ORDER_STATE_TRANSITION,
-                },
-            },
-            sort: {
-                createdAt: SortOrder.DESC,
-            },
-        })
-        .mapSingle(result => result.order)
-        .pipe(
-            switchMap(result => {
-                const item = result?.history.items.find(i => i.data.to === 'Modifying');
-                if (item) {
-                    const originalState = item.data.from;
-                    return dataService.order.transitionToState(orderId, originalState);
-                } else {
-                    return EMPTY;
-                }
-            }),
-        );
-}

+ 2 - 2
packages/admin-ui/src/lib/order/src/components/order-editor/order-editor.component.html

@@ -195,11 +195,11 @@
                         <vdr-product-selector class="mb4" (productSelected)="addItemSelectedVariant = $event">
                         </vdr-product-selector>
                         <div *ngIf="addItemSelectedVariant" class="flex mb4">
-                            <img [src]="addItemSelectedVariant.productAsset | assetPreview: 'tiny'" class="mr4" />
+                            <img *ngIf="addItemSelectedVariant.productAsset as asset" [src]="asset | assetPreview: 'tiny'" class="mr4" />
                             <div>
                                 <strong class="mr4">{{ addItemSelectedVariant.productVariantName }}</strong>
                                 <small>{{ addItemSelectedVariant.sku }}</small>
-                                <div>{{ (addItemSelectedVariant.priceWithTax.value / 100) | currency: order.currencyCode }}</div>
+                                <div>{{ getSelectedItemPrice(addItemSelectedVariant) / 100 | currency: order.currencyCode }}</div>
                             </div>
 
                         </div>

+ 20 - 11
packages/admin-ui/src/lib/order/src/components/order-editor/order-editor.component.ts

@@ -24,7 +24,7 @@ import { assertNever, notNullOrUndefined } from '@vendure/common/lib/shared-util
 import { EMPTY, Observable, of } from 'rxjs';
 import { mapTo, shareReplay, switchMap, takeUntil } from 'rxjs/operators';
 
-import { transitionToPreModifyingState } from '../order-detail/transition-to-pre-modifying-state';
+import { OrderTransitionService } from '../../providers/order-transition.service';
 import {
     OrderEditResultType,
     OrderEditsPreviewDialogComponent,
@@ -88,6 +88,7 @@ export class OrderEditorComponent
         protected dataService: DataService,
         private notificationService: NotificationService,
         private modalService: ModalService,
+        private orderTransitionService: OrderTransitionService,
     ) {
         super(route, router, serverConfigService, dataService);
     }
@@ -195,15 +196,11 @@ export class OrderEditorComponent
     }
 
     transitionToPriorState(order: OrderDetail.Fragment) {
-        transitionToPreModifyingState(this.dataService, order.id).subscribe(({ transitionOrderToState }) => {
-            switch (transitionOrderToState?.__typename) {
-                case 'Order':
-                    this.router.navigate(['..'], { relativeTo: this.route });
-                    break;
-                case 'OrderStateTransitionError':
-                    this.notificationService.error(transitionOrderToState?.transitionError);
-            }
-        });
+        this.orderTransitionService
+            .transitionToPreModifyingState(order.id, order.nextStates)
+            .subscribe(result => {
+                this.router.navigate(['..'], { relativeTo: this.route });
+            });
     }
 
     canPreviewChanges(): boolean {
@@ -250,7 +247,19 @@ export class OrderEditorComponent
         return item.productVariantId;
     }
 
-    addItemToOrder(result: ProductSelectorSearch.Items) {
+    getSelectedItemPrice(result: ProductSelectorSearch.Items | undefined): number {
+        switch (result?.priceWithTax.__typename) {
+            case 'SinglePrice':
+                return result.priceWithTax.value;
+            default:
+                return 0;
+        }
+    }
+
+    addItemToOrder(result: ProductSelectorSearch.Items | undefined) {
+        if (!result) {
+            return;
+        }
         const customFields = this.addItemCustomFieldsForm.value;
         let row = this.modifyOrderInput.addItems?.find(l =>
             this.isMatchingAddItemRow(l, result, customFields),

+ 17 - 0
packages/admin-ui/src/lib/order/src/components/order-state-select-dialog/order-state-select-dialog.component.html

@@ -0,0 +1,17 @@
+<ng-template vdrDialogTitle>{{ 'order.select-state' | translate }}</ng-template>
+<p>{{ message | translate }}</p>
+<clr-select-container>
+    <select clrSelect name="state" [(ngModel)]="selectedState">
+        <option *ngFor="let state of nextStates" [value]="state">
+            {{ state | stateI18nToken | translate }}
+        </option>
+    </select>
+</clr-select-container>
+<ng-template vdrDialogButtons>
+    <button type="submit" *ngIf="cancellable" (click)="cancel()" class="btn btn-secondary">
+        {{ 'common.cancel' | translate }}
+    </button>
+    <button type="submit" (click)="select()" class="btn btn-primary" [disabled]="!selectedState">
+        {{ 'order.transition-to-state' | translate: { state: selectedState } }}
+    </button>
+</ng-template>

+ 0 - 0
packages/admin-ui/src/lib/order/src/components/order-state-select-dialog/order-state-select-dialog.component.scss


+ 26 - 0
packages/admin-ui/src/lib/order/src/components/order-state-select-dialog/order-state-select-dialog.component.ts

@@ -0,0 +1,26 @@
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { Dialog } from '@vendure/admin-ui/core';
+
+@Component({
+    selector: 'vdr-order-state-select-dialog',
+    templateUrl: './order-state-select-dialog.component.html',
+    styleUrls: ['./order-state-select-dialog.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class OrderStateSelectDialogComponent implements Dialog<string> {
+    resolveWith: (result?: string) => void;
+    nextStates: string[] = [];
+    message = '';
+    cancellable: boolean;
+    selectedState = '';
+
+    select() {
+        if (this.selectedState) {
+            this.resolveWith(this.selectedState);
+        }
+    }
+
+    cancel() {
+        this.resolveWith();
+    }
+}

+ 2 - 0
packages/admin-ui/src/lib/order/src/order.module.ts

@@ -22,6 +22,7 @@ import { OrderProcessGraphDialogComponent } from './components/order-process-gra
 import { OrderProcessEdgeComponent } from './components/order-process-graph/order-process-edge.component';
 import { OrderProcessGraphComponent } from './components/order-process-graph/order-process-graph.component';
 import { OrderProcessNodeComponent } from './components/order-process-graph/order-process-node.component';
+import { OrderStateSelectDialogComponent } from './components/order-state-select-dialog/order-state-select-dialog.component';
 import { OrderTableComponent } from './components/order-table/order-table.component';
 import { PaymentDetailComponent } from './components/payment-detail/payment-detail.component';
 import { PaymentStateLabelComponent } from './components/payment-state-label/payment-state-label.component';
@@ -61,6 +62,7 @@ import { orderRoutes } from './order.routes';
         OrderEditsPreviewDialogComponent,
         ModificationDetailComponent,
         AddManualPaymentDialogComponent,
+        OrderStateSelectDialogComponent,
     ],
 })
 export class OrderModule {}

+ 133 - 0
packages/admin-ui/src/lib/order/src/providers/order-transition.service.ts

@@ -0,0 +1,133 @@
+import { Injectable } from '@angular/core';
+import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
+import {
+    DataService,
+    HistoryEntryType,
+    I18nService,
+    ModalService,
+    NotificationService,
+    SortOrder,
+} from '@vendure/admin-ui/core';
+import { EMPTY } from 'rxjs';
+import { catchError, delay, map, retryWhen, switchMap, take } from 'rxjs/operators';
+
+import { OrderStateSelectDialogComponent } from '../components/order-state-select-dialog/order-state-select-dialog.component';
+
+@Injectable({
+    providedIn: 'root',
+})
+export class OrderTransitionService {
+    constructor(
+        private dataService: DataService,
+        private modalService: ModalService,
+        private notificationService: NotificationService,
+        private i18nService: I18nService,
+    ) {}
+
+    /**
+     * Attempts to transition the Order to the last state it was in before it was transitioned
+     * to the "Modifying" state. If this fails, a manual prompt is used.
+     */
+    transitionToPreModifyingState(orderId: string, nextStates: string[]) {
+        return this.getPreModifyingState(orderId).pipe(
+            switchMap(state => {
+                const manualTransitionOptions = {
+                    orderId,
+                    nextStates,
+                    message: this.i18nService.translate(
+                        _('order.unable-to-transition-to-state-try-another'),
+                        { state },
+                    ),
+                    cancellable: false,
+                    retry: 10,
+                };
+                if (state) {
+                    return this.transitionToStateOrThrow(orderId, state).pipe(
+                        catchError(err => this.manuallyTransitionToState(manualTransitionOptions)),
+                    );
+                } else {
+                    return this.manuallyTransitionToState(manualTransitionOptions);
+                }
+            }),
+        );
+    }
+
+    /**
+     * Displays a modal for manually selecting the next state.
+     */
+    manuallyTransitionToState(options: {
+        orderId: string;
+        nextStates: string[];
+        message: string;
+        cancellable: boolean;
+        retry: number;
+    }) {
+        return this.modalService
+            .fromComponent(OrderStateSelectDialogComponent, {
+                locals: {
+                    nextStates: options.nextStates,
+                    cancellable: options.cancellable,
+                    message: options.message,
+                },
+                closable: false,
+                size: 'md',
+            })
+            .pipe(
+                switchMap(result => {
+                    if (result) {
+                        return this.transitionToStateOrThrow(options.orderId, result);
+                    } else {
+                        if (!options.cancellable) {
+                            throw new Error(`An order state must be selected`);
+                        } else {
+                            return EMPTY;
+                        }
+                    }
+                }),
+                retryWhen(errors => errors.pipe(delay(2000), take(options.retry))),
+            );
+    }
+
+    /**
+     * Attempts to get the last state the Order was in before it was transitioned
+     * to the "Modifying" state.
+     */
+    private getPreModifyingState(orderId: string) {
+        return this.dataService.order
+            .getOrderHistory(orderId, {
+                filter: {
+                    type: {
+                        eq: HistoryEntryType.ORDER_STATE_TRANSITION,
+                    },
+                },
+                sort: {
+                    createdAt: SortOrder.DESC,
+                },
+            })
+            .mapSingle(result => result.order)
+            .pipe(
+                map(result => {
+                    const item = result?.history.items.find(i => i.data.to === 'Modifying');
+                    if (item) {
+                        return item.data.from as string;
+                    } else {
+                        return;
+                    }
+                }),
+            );
+    }
+
+    private transitionToStateOrThrow(orderId: string, state: string) {
+        return this.dataService.order.transitionToState(orderId, state).pipe(
+            map(({ transitionOrderToState }) => {
+                switch (transitionOrderToState?.__typename) {
+                    case 'Order':
+                        return transitionOrderToState?.state;
+                    case 'OrderStateTransitionError':
+                        this.notificationService.error(transitionOrderToState?.transitionError);
+                        throw new Error(transitionOrderToState?.transitionError);
+                }
+            }),
+        );
+    }
+}

+ 4 - 0
packages/admin-ui/src/lib/static/i18n-messages/cs.json

@@ -603,6 +603,8 @@
     "line-fulfillment-all": "Všechny položky zpracovány",
     "line-fulfillment-none": "Žádné položky zpracovány",
     "line-fulfillment-partial": "{ count } z { total } položek zpracováno",
+    "manually-transition-to-state": "",
+    "manually-transition-to-state-message": "",
     "modification-adding-items": "",
     "modification-adding-surcharges": "",
     "modification-adjusting-lines": "",
@@ -654,6 +656,7 @@
     "refunded-count": "{count} {count, plural, one {položka} other {položky}} refundovány",
     "removed-items": "",
     "search-by-order-code": "Hledat na základě kódu objednávky",
+    "select-state": "",
     "set-fulfillment-state": "Označit jako {state}",
     "settle-payment": "Vypořádání platby",
     "settle-payment-error": "Nelze vyřídit platbu",
@@ -678,6 +681,7 @@
     "transaction-id": "ID transakce",
     "transition-to-state": "Změna stavu na { state }",
     "transitioned-to-state-success": "Stav úspěšně změněn na { state }",
+    "unable-to-transition-to-state-try-another": "",
     "unfulfilled": "Nevyřízeno",
     "unit-price": "Cena za kus"
   },

+ 4 - 0
packages/admin-ui/src/lib/static/i18n-messages/de.json

@@ -603,6 +603,8 @@
     "line-fulfillment-all": "Alle Positionen ausgeführt",
     "line-fulfillment-none": "Keine Positionen ausgeführt",
     "line-fulfillment-partial": "{ count } von { total } Positionen ausgeführt",
+    "manually-transition-to-state": "",
+    "manually-transition-to-state-message": "",
     "modification-adding-items": "",
     "modification-adding-surcharges": "",
     "modification-adjusting-lines": "",
@@ -654,6 +656,7 @@
     "refunded-count": "{count} {count, plural, one {Artikel} other {Artikel}} erstattet",
     "removed-items": "",
     "search-by-order-code": "Suche nach Bestellcode",
+    "select-state": "",
     "set-fulfillment-state": "",
     "settle-payment": "Zahlung durchführen",
     "settle-payment-error": "Die Zahlung konnte nicht durchgeführt werden",
@@ -678,6 +681,7 @@
     "transaction-id": "Transaktions-ID",
     "transition-to-state": "",
     "transitioned-to-state-success": "",
+    "unable-to-transition-to-state-try-another": "",
     "unfulfilled": "Nicht ausgeführt",
     "unit-price": "Einzelpreis"
   },

+ 4 - 0
packages/admin-ui/src/lib/static/i18n-messages/en.json

@@ -603,6 +603,8 @@
     "line-fulfillment-all": "All items fulfilled",
     "line-fulfillment-none": "No items fulfilled",
     "line-fulfillment-partial": "{ count } of { total } items fulfilled",
+    "manually-transition-to-state": "Manually transition to state...",
+    "manually-transition-to-state-message": "Manually transition the order to another state. Note that order states are governed by rules which may prevent certain transitions.",
     "modification-adding-items": "Adding {count} {count, plural, one {item} other {items}}",
     "modification-adding-surcharges": "Adding {count} {count, plural, one {surcharge} other {surcharges}}",
     "modification-adjusting-lines": "Adjusting {count} {count, plural, one {line} other {lines}}",
@@ -654,6 +656,7 @@
     "refunded-count": "{count} {count, plural, one {item} other {items}} refunded",
     "removed-items": "Removed items",
     "search-by-order-code": "Search by order code",
+    "select-state": "Select state",
     "set-fulfillment-state": "Mark as {state}",
     "settle-payment": "Settle payment",
     "settle-payment-error": "Could not settle payment",
@@ -678,6 +681,7 @@
     "transaction-id": "Transaction ID",
     "transition-to-state": "Transition to { state } state",
     "transitioned-to-state-success": "Successfully transitioned to { state }",
+    "unable-to-transition-to-state-try-another": "The order could not be transitioned back to the \"{state}\" state. Please select an alternative state.",
     "unfulfilled": "Unfulfilled",
     "unit-price": "Unit price"
   },

+ 4 - 0
packages/admin-ui/src/lib/static/i18n-messages/es.json

@@ -603,6 +603,8 @@
     "line-fulfillment-all": "",
     "line-fulfillment-none": "",
     "line-fulfillment-partial": "",
+    "manually-transition-to-state": "",
+    "manually-transition-to-state-message": "",
     "modification-adding-items": "",
     "modification-adding-surcharges": "",
     "modification-adjusting-lines": "",
@@ -654,6 +656,7 @@
     "refunded-count": "",
     "removed-items": "",
     "search-by-order-code": "",
+    "select-state": "",
     "set-fulfillment-state": "",
     "settle-payment": "",
     "settle-payment-error": "",
@@ -678,6 +681,7 @@
     "transaction-id": "ID de transacción",
     "transition-to-state": "",
     "transitioned-to-state-success": "",
+    "unable-to-transition-to-state-try-another": "",
     "unfulfilled": "",
     "unit-price": "Precio unitario"
   },

+ 4 - 0
packages/admin-ui/src/lib/static/i18n-messages/fr.json

@@ -603,6 +603,8 @@
     "line-fulfillment-all": "Tous les articles préparés",
     "line-fulfillment-none": "Aucun article préparé",
     "line-fulfillment-partial": "{ count } sur { total } {count, plural, one {article préparé} other {articles préparés}}",
+    "manually-transition-to-state": "",
+    "manually-transition-to-state-message": "",
     "modification-adding-items": "",
     "modification-adding-surcharges": "",
     "modification-adjusting-lines": "",
@@ -654,6 +656,7 @@
     "refunded-count": "{count} {count, plural, one {article remboursé} other {articles remboursés}}",
     "removed-items": "",
     "search-by-order-code": "Chercher par numéro de commande",
+    "select-state": "",
     "set-fulfillment-state": "Marquer {state}",
     "settle-payment": "Régler le paiement",
     "settle-payment-error": "Règlement du paiement échoué",
@@ -678,6 +681,7 @@
     "transaction-id": "Numéro de transaction",
     "transition-to-state": "Passage à l'état: { state }",
     "transitioned-to-state-success": "Passage à l'état { state } avec succés",
+    "unable-to-transition-to-state-try-another": "",
     "unfulfilled": "Non préparé",
     "unit-price": "Prix à l'unité"
   },

+ 4 - 0
packages/admin-ui/src/lib/static/i18n-messages/pl.json

@@ -603,6 +603,8 @@
     "line-fulfillment-all": "Wrzystkie zamówienia zrealizowane",
     "line-fulfillment-none": "Brak zrealizowany zamówień",
     "line-fulfillment-partial": "{ count } z { total } zrealizowanych",
+    "manually-transition-to-state": "",
+    "manually-transition-to-state-message": "",
     "modification-adding-items": "",
     "modification-adding-surcharges": "",
     "modification-adjusting-lines": "",
@@ -654,6 +656,7 @@
     "refunded-count": "{count} {count, plural, one {zamówienie} other {zamówień}} zwrócono",
     "removed-items": "",
     "search-by-order-code": "Szukaj po numerze zamówienia",
+    "select-state": "",
     "set-fulfillment-state": "",
     "settle-payment": "Rozlicz płatność",
     "settle-payment-error": "Nie można rozliczyć płatności",
@@ -678,6 +681,7 @@
     "transaction-id": "Numer transakcji",
     "transition-to-state": "",
     "transitioned-to-state-success": "",
+    "unable-to-transition-to-state-try-another": "",
     "unfulfilled": "Unfulfilled",
     "unit-price": "Cena jednostkowa"
   },

+ 4 - 0
packages/admin-ui/src/lib/static/i18n-messages/pt_BR.json

@@ -603,6 +603,8 @@
     "line-fulfillment-all": "Todos os itens executados",
     "line-fulfillment-none": "Nenhum ítem executado",
     "line-fulfillment-partial": "{ count } of { total } itens executados",
+    "manually-transition-to-state": "",
+    "manually-transition-to-state-message": "",
     "modification-adding-items": "",
     "modification-adding-surcharges": "",
     "modification-adjusting-lines": "",
@@ -654,6 +656,7 @@
     "refunded-count": "{count} {count, plural, one {item} other {items}} reembolsado",
     "removed-items": "",
     "search-by-order-code": "Buscar por código do pedido",
+    "select-state": "",
     "set-fulfillment-state": "",
     "settle-payment": "Liquidar pagamento",
     "settle-payment-error": "Não posso liquidar pagamento",
@@ -678,6 +681,7 @@
     "transaction-id": "Código ID da transação",
     "transition-to-state": "Mudança para o estado { state }",
     "transitioned-to-state-success": "Mudança para o estado { state } realizada com sucesso",
+    "unable-to-transition-to-state-try-another": "",
     "unfulfilled": "Não realizado",
     "unit-price": "Preço unitário"
   },

+ 4 - 0
packages/admin-ui/src/lib/static/i18n-messages/zh_Hans.json

@@ -603,6 +603,8 @@
     "line-fulfillment-all": "订单已全部配货完成",
     "line-fulfillment-none": "无订单配货记录",
     "line-fulfillment-partial": "总共{ total }个订单项,{ count }个已配货",
+    "manually-transition-to-state": "",
+    "manually-transition-to-state-message": "",
     "modification-adding-items": "",
     "modification-adding-surcharges": "",
     "modification-adjusting-lines": "",
@@ -654,6 +656,7 @@
     "refunded-count": "{count}个商品已退款",
     "removed-items": "",
     "search-by-order-code": "输入要搜索的订单编号",
+    "select-state": "",
     "set-fulfillment-state": "",
     "settle-payment": "结算付款",
     "settle-payment-error": "结算付款失败",
@@ -678,6 +681,7 @@
     "transaction-id": "交易ID",
     "transition-to-state": "",
     "transitioned-to-state-success": "",
+    "unable-to-transition-to-state-try-another": "",
     "unfulfilled": "未配货",
     "unit-price": "单价"
   },

+ 4 - 0
packages/admin-ui/src/lib/static/i18n-messages/zh_Hant.json

@@ -603,6 +603,8 @@
     "line-fulfillment-all": "訂單已全部配貨完成",
     "line-fulfillment-none": "無訂單配貨記錄",
     "line-fulfillment-partial": "總共{ total }個訂單項,{ count }個已配貨",
+    "manually-transition-to-state": "",
+    "manually-transition-to-state-message": "",
     "modification-adding-items": "",
     "modification-adding-surcharges": "",
     "modification-adjusting-lines": "",
@@ -654,6 +656,7 @@
     "refunded-count": "{count}個商品已退款",
     "removed-items": "",
     "search-by-order-code": "輸入要搜索的訂單編號",
+    "select-state": "",
     "set-fulfillment-state": "",
     "settle-payment": "結算付款",
     "settle-payment-error": "結算付款失敗",
@@ -678,6 +681,7 @@
     "transaction-id": "交易編號",
     "transition-to-state": "",
     "transitioned-to-state-success": "",
+    "unable-to-transition-to-state-try-another": "",
     "unfulfilled": "未配貨",
     "unit-price": "單價"
   },