Skip to content

Commit

Permalink
Merge branch 'trunk' into issue/13551-packages-data
Browse files Browse the repository at this point in the history
  • Loading branch information
bozidarsevo committed Nov 7, 2024
2 parents e86e14e + 4ef0fe0 commit 12ff1f9
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,11 @@ private extension DefaultProductFormTableViewModel {
inventoryDetails.append(String.localizedStringWithFormat(Localization.skuFormat, sku))
}

if featureFlagService.isFeatureFlagEnabled(.productGlobalUniqueIdentifierSupport),
let globalUniqueID = product.globalUniqueID, !globalUniqueID.isEmpty {
inventoryDetails.append(String.localizedStringWithFormat(Localization.globalUniqueIDFormat, globalUniqueID))
}

if let stockQuantity = product.stockQuantity, product.manageStock {
let localizedStockQuantity = NumberFormatter.localizedString(from: stockQuantity as NSDecimalNumber, number: .decimal)
inventoryDetails.append(String.localizedStringWithFormat(Localization.stockQuantityFormat, localizedStockQuantity))
Expand Down Expand Up @@ -813,6 +818,8 @@ private extension DefaultProductFormTableViewModel.Localization {
// Inventory
static let skuFormat = NSLocalizedString("SKU: %@",
comment: "Format of the SKU on the Inventory Settings row")
static let globalUniqueIDFormat = NSLocalizedString("GTIN, UPC, EAN, ISBN: %@",
comment: "Format of the Global Unique Identifier on the Inventory Settings row")
static let stockQuantityFormat = NSLocalizedString("Quantity: %@",
comment: "Format of the stock quantity on the Inventory Settings row")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,6 @@ extension ProductInventorySettingsViewController: UITableViewDelegate {
fatalError()
}
headerView.configure(title: errorTitle)
headerView.addTopSpacing()
UIAccessibility.post(notification: .layoutChanged, argument: headerView)
return headerView
}
Expand Down Expand Up @@ -280,12 +279,12 @@ private extension ProductInventorySettingsViewController {
self?.handleSKUValidation(isValid: isValid, shouldBringUpKeyboard: shouldBringUpKeyboard)
}
}
switch viewModel.error {
case .duplicatedSKU, .invalidSKU:

let thereIsAnSKUError = viewModel.errors.contains(.duplicatedSKU) || viewModel.errors.contains(.invalidSKU)
if thereIsAnSKUError {
cellViewModel = cellViewModel.stateUpdated(state: .error)
default:
break
}

cell.configure(viewModel: cellViewModel)
cell.setSpacingBetweenTitleAndTextField(30)
cell.setKeyboardType(keyboardType: .default)
Expand Down Expand Up @@ -315,7 +314,7 @@ private extension ProductInventorySettingsViewController {
}
}

if viewModel.error == .invalidGlobalUniqueIdentifier {
if viewModel.errors.contains(.invalidGlobalUniqueIdentifier) {
cellViewModel = cellViewModel.stateUpdated(state: .error)
}

Expand Down Expand Up @@ -399,7 +398,7 @@ private extension ProductInventorySettingsViewController {

startBarcodeScanning(onCompletion: { [weak self] barcode in
ServiceLocator.analytics.track(.productInventorySettingsGlobalUniqueIDScanned)
self?.viewModel.handleGlobalUniqueIdentifierFromBarcodeScanner("123as", onValidation: {[weak self] isValid, shouldBringUpKeyboard in
self?.viewModel.handleGlobalUniqueIdentifierFromBarcodeScanner(barcode, onValidation: {[weak self] isValid, shouldBringUpKeyboard in
self?.handleGlobalUniqueIdentifierValidation(isValid: isValid, shouldBringUpKeyboard: shouldBringUpKeyboard)})
})
}
Expand Down Expand Up @@ -427,14 +426,14 @@ private extension ProductInventorySettingsViewController {

private extension ProductInventorySettingsViewController {
func handleSKUValidation(isValid: Bool, shouldBringUpKeyboard: Bool) {
enableDoneButton(isValid)
enableDoneButton(viewModel.errors.isEmpty)
if shouldBringUpKeyboard {
getTitleAndTextFieldCell(from: .sku)?.textFieldBecomeFirstResponder()
}
}

func handleGlobalUniqueIdentifierValidation(isValid: Bool, shouldBringUpKeyboard: Bool) {
enableDoneButton(isValid)
enableDoneButton(viewModel.errors.isEmpty)
if shouldBringUpKeyboard {
getTitleAndTextFieldCell(from: .globalUniqueIdentifier)?.textFieldBecomeFirstResponder()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ protocol ProductInventorySettingsViewModelOutput {
var sections: AnyPublisher<[Section], Never> { get }

/// Potential error from input changes.
var error: ProductUpdateError? { get }
var errors: [ProductUpdateError] { get }

/// The type of inventory form.
var formType: ProductInventorySettingsViewController.FormType { get }
Expand Down Expand Up @@ -92,7 +92,7 @@ final class ProductInventorySettingsViewModel: ProductInventorySettingsViewModel
}
@Published private var sectionsSubject: [Section] = []

private(set) var error: ProductUpdateError?
private(set) var errors: [ProductUpdateError] = []

// Sku validation
private var skuIsValid: Bool = true
Expand Down Expand Up @@ -134,7 +134,7 @@ extension ProductInventorySettingsViewModel: ProductInventorySettingsActionHandl
// If the sku is identical to the old one, is always valid
guard sku != productModel.sku else {
skuIsValid = true
hideError()
hideError(error: .duplicatedSKU)
throttler.cancel()
onValidation(true, true)
return
Expand All @@ -155,7 +155,7 @@ extension ProductInventorySettingsViewModel: ProductInventorySettingsActionHandl
onValidation(false, true)
return
}
self.hideError()
self.hideError(error: .duplicatedSKU)
onValidation(true, false)
}

Expand All @@ -172,7 +172,7 @@ extension ProductInventorySettingsViewModel: ProductInventorySettingsActionHandl

var shouldBringUpKeyboard = false
// If the error was already shown there's no need to show it again
if error != .invalidGlobalUniqueIdentifier {
if !errors.contains(.invalidGlobalUniqueIdentifier) {
displayError(error: .invalidGlobalUniqueIdentifier)
// After reloading the sections the keyboard was dismissed, let's bring it back again
shouldBringUpKeyboard = true
Expand All @@ -185,8 +185,8 @@ extension ProductInventorySettingsViewModel: ProductInventorySettingsActionHandl
}

// Bring keyboard up if the error was shown, so they user can keep typing when the sections are reloaded to remove the error message
let shouldBringUpKeyboard = error == .invalidGlobalUniqueIdentifier
hideError()
let shouldBringUpKeyboard = errors.contains(.invalidGlobalUniqueIdentifier)
hideError(error: .invalidGlobalUniqueIdentifier)
globalUniqueIdIsValid = true
onValidation(true, shouldBringUpKeyboard)
}
Expand Down Expand Up @@ -272,9 +272,12 @@ extension ProductInventorySettingsViewModel: ProductInventorySettingsActionHandl
//
private extension ProductInventorySettingsViewModel {
func reloadSections() {
let sections: [Section]
switch formType {
case .inventory:
var sections = [createSKUSection()]
if featureFlagService.isFeatureFlagEnabled(.productGlobalUniqueIdentifierSupport) {
sections.append(createGlobalUniqueIdentifierSection())
}

if formType == .inventory {
let stockSection: Section
if manageStockEnabled {
stockSection = Section(rows: [.manageStock, .stockQuantity, .backorders])
Expand All @@ -284,40 +287,31 @@ private extension ProductInventorySettingsViewModel {
stockSection = Section(rows: [.manageStock])
}

switch productModel {
case is EditableProductModel:
sections = [
createSKUSection(),
stockSection,
Section(rows: [.limitOnePerOrder])
]
case is EditableProductVariationModel:
sections = [
createSKUSection(),
stockSection
]
default:
fatalError("Unsupported product type: \(productModel)")
sections.append(stockSection)

if productModel is EditableProductModel {
sections.append(Section(rows: [.limitOnePerOrder]))
}
case .sku:
sections = [
createSKUSection()
]
}

sectionsSubject = sections
}

func createSKUSection() -> Section {
var rows: [ProductInventorySettingsViewController.Row] = [.sku]
let skuError = errors.first { $0 == .invalidSKU || $0 == .duplicatedSKU }

if featureFlagService.isFeatureFlagEnabled(.productGlobalUniqueIdentifierSupport) {
rows.append(.globalUniqueIdentifier)
if let skuError = skuError {
return Section(errorTitle: skuError.errorDescription, rows: [.sku])
} else {
return Section(rows: [.sku])
}
}

if let error = error {
return Section(errorTitle: error.errorDescription, rows: rows)
func createGlobalUniqueIdentifierSection() -> Section {
if errors.contains(.invalidGlobalUniqueIdentifier) {
return Section(errorTitle: ProductUpdateError.invalidGlobalUniqueIdentifier.errorDescription, rows: [.globalUniqueIdentifier])
} else {
return Section(rows: rows)
return Section(rows: [.globalUniqueIdentifier])
}
}
}
Expand All @@ -326,14 +320,14 @@ private extension ProductInventorySettingsViewModel {
//
private extension ProductInventorySettingsViewModel {
func displayError(error: ProductUpdateError) {
self.error = error
errors.append(error)
reloadSections()
}

func hideError() {
func hideError(error: ProductUpdateError) {
// This check is useful so we don't reload while typing each letter in the sections
if error != nil {
error = nil
if errors.contains(error) {
errors.removeAll { $0 == error }
reloadSections()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import UIKit
/// Displays error for the table view section.
///
final class ErrorSectionHeaderView: UITableViewHeaderFooterView {

@IBOutlet weak var titleLabelTopSpacing: NSLayoutConstraint!
@IBOutlet private weak var titleLabel: UILabel!

// MARK: - Overridden Methods
Expand All @@ -19,10 +17,6 @@ final class ErrorSectionHeaderView: UITableViewHeaderFooterView {
func configure(title: String?) {
titleLabel.text = title
}

func addTopSpacing() {
titleLabelTopSpacing.constant = 22
}
}

/// Configurations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
</constraints>
<connections>
<outlet property="titleLabel" destination="1wy-6U-sOR" id="JWJ-K9-bMr"/>
<outlet property="titleLabelTopSpacing" destination="kIo-sI-dZ4" id="Att-hf-G4G"/>
</connections>
<point key="canvasLocation" x="139" y="98"/>
</view>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,41 @@ final class DefaultProductFormTableViewModelTests: XCTestCase {
XCTAssertNil(inventoryViewModel?.details)
}

func test_variable_product_inventory_row_has_details_when_passing_global_unique_identifier() {
// Arrange
let globalUniqueID = "12345"
let product = Product.fake().copy(productTypeKey: ProductType.variable.rawValue,
sku: "",
globalUniqueID: globalUniqueID,
manageStock: false,
stockStatusKey: ProductStockStatus.onBackOrder.rawValue)
let model = EditableProductModel(product: product)
let actionsFactory = ProductFormActionsFactory(product: model, formType: .edit)

// Action
let featureFlagService = MockFeatureFlagService(isProductGlobalUniqueIdentifierSupported: true)
let tableViewModel = DefaultProductFormTableViewModel(product: model,
actionsFactory: actionsFactory,
currency: "",
isDescriptionAIEnabled: true,
featureFlagService: featureFlagService)

// Assert
guard case let .settings(rows) = tableViewModel.sections[1] else {
XCTFail("Unexpected section at index 1: \(tableViewModel.sections)")
return
}
var inventoryViewModel: ProductFormSection.SettingsRow.ViewModel?
for row in rows {
if case let .inventory(viewModel, _) = row {
inventoryViewModel = viewModel
break
}
}
let expectedDetails = "GTIN, UPC, EAN, ISBN: \(globalUniqueID)"
XCTAssertEqual(inventoryViewModel?.details, expectedDetails)
}

func test_variation_view_model_image_row_has_isVariation_true() {
// Arrange
let variation = ProductVariation.fake()
Expand Down
Loading

0 comments on commit 12ff1f9

Please sign in to comment.