Class: ListingIssue

Inherits:
ApplicationRecord show all
Defined in:
app/models/listing_issue.rb

Overview

A single problem with a catalog item's marketplace listing as reported by the
marketplace's own API — Amazon suppression / SP-API listing issues, Walmart
unpublished reasons, and (as integrations land) other providers.

Distinct from CatalogItemRetailerProbe, which scrapes the public retailer
page: a listing issue is what the channel says is wrong with our listing,
not what our scraper could or couldn't read. Rows are reconciled by
Catalog::ListingIssueSync (per provider): current issues are upserted on the
(catalog_item_id, provider, fingerprint) key and issues no longer reported
are auto-resolved, so the open set always mirrors the channel. Surfaced as the
"Listing Issues" column in the daily retailer-compliance report and reviewed /
resolved in the CRM Listing Issues dashboard.

Constant Summary collapse

PROVIDERS =

Recognised providers (the marketplace the issue came from).

%w[amazon walmart].freeze
SEVERITIES =

Severity buckets. error blocks/suppresses the listing; warning is
advisory (the listing still shows).

%w[error warning].freeze

Constants included from Schedulable

Schedulable::SIMPLE_FORM_OPTIONS

Instance Attribute Summary collapse

Belongs to collapse

Has one collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation

Methods included from Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#codeObject (readonly)



64
# File 'app/models/listing_issue.rb', line 64

validates :code, presence: true

#fingerprintObject (readonly)



66
# File 'app/models/listing_issue.rb', line 66

validates :fingerprint, presence: true, uniqueness: { scope: %i[catalog_item_id provider] }

#providerObject (readonly)



63
# File 'app/models/listing_issue.rb', line 63

validates :provider, presence: true, inclusion: { in: PROVIDERS }

#severityObject (readonly)



65
# File 'app/models/listing_issue.rb', line 65

validates :severity, inclusion: { in: SEVERITIES }

Class Method Details

.by_skuActiveRecord::Relation<ListingIssue>

A relation of ListingIssues that are by sku. Active Record Scope

Returns:

See Also:



77
78
79
80
81
# File 'app/models/listing_issue.rb', line 77

scope :by_sku, ->(sku) {
  return all if sku.blank?

  joins(catalog_item: { store_item: :item }).where('items.sku ILIKE ?', "%#{sku.strip}%")
}

.errorsActiveRecord::Relation<ListingIssue>

A relation of ListingIssues that are errors. Active Record Scope

Returns:

See Also:



71
# File 'app/models/listing_issue.rb', line 71

scope :errors,       -> { where(severity: 'error') }

.for_providerActiveRecord::Relation<ListingIssue>

A relation of ListingIssues that are for provider. Active Record Scope

Returns:

See Also:



70
# File 'app/models/listing_issue.rb', line 70

scope :for_provider, ->(provider) { where(provider:) }

.openActiveRecord::Relation<ListingIssue>

A relation of ListingIssues that are open. Active Record Scope

Returns:

See Also:



68
# File 'app/models/listing_issue.rb', line 68

scope :open,         -> { where(resolved_at: nil) }

.open_item_idsActiveRecord::Relation<ListingIssue>

A relation of ListingIssues that are open item ids. Active Record Scope

Returns:

See Also:



75
# File 'app/models/listing_issue.rb', line 75

scope :open_item_ids, -> { open.distinct.select(:catalog_item_id) }

.resolvedActiveRecord::Relation<ListingIssue>

A relation of ListingIssues that are resolved. Active Record Scope

Returns:

See Also:



69
# File 'app/models/listing_issue.rb', line 69

scope :resolved,     -> { where.not(resolved_at: nil) }

.warningsActiveRecord::Relation<ListingIssue>

A relation of ListingIssues that are warnings. Active Record Scope

Returns:

See Also:



72
# File 'app/models/listing_issue.rb', line 72

scope :warnings,     -> { where(severity: 'warning') }

Instance Method Details

#catalogCatalog

Returns:

See Also:



61
# File 'app/models/listing_issue.rb', line 61

has_one :catalog, through: :catalog_item

#catalog_itemCatalogItem



58
# File 'app/models/listing_issue.rb', line 58

belongs_to :catalog_item

#open?Boolean

Returns true while the issue is still open (unresolved).

Returns:

  • (Boolean)

    true while the issue is still open (unresolved).



84
85
86
# File 'app/models/listing_issue.rb', line 84

def open?
  resolved_at.nil?
end

#reopen!Boolean

Re-open a previously resolved issue (used when a sync sees it recur).

Returns:

  • (Boolean)


103
104
105
# File 'app/models/listing_issue.rb', line 103

def reopen!
  update!(resolved_at: nil, resolved_by: nil, resolution_note: nil)
end

#resolve!(by: nil, note: nil) ⇒ Boolean

Mark the issue resolved.

Parameters:

  • by (EmployeeRecord, nil) (defaults to: nil)

    who resolved it (manual review)

  • note (String, nil) (defaults to: nil)

    optional resolution note

Returns:

  • (Boolean)


97
98
99
# File 'app/models/listing_issue.rb', line 97

def resolve!(by: nil, note: nil)
  update!(resolved_at: Time.current, resolved_by: by, resolution_note: note)
end

#resolved?Boolean

Returns true once the issue has been resolved.

Returns:

  • (Boolean)

    true once the issue has been resolved.



89
90
91
# File 'app/models/listing_issue.rb', line 89

def resolved?
  resolved_at.present?
end

#resolved_byEmployeeRecord



59
# File 'app/models/listing_issue.rb', line 59

belongs_to :resolved_by, class_name: 'EmployeeRecord', optional: true