diff --git a/svc/product_series_auto_scheduler.go b/svc/product_series_auto_scheduler.go index b9c51b5..a847ec4 100644 --- a/svc/product_series_auto_scheduler.go +++ b/svc/product_series_auto_scheduler.go @@ -248,6 +248,11 @@ WHERE NOT (row_key = ANY($1)) } stats.Changed++ } + retryCount, err := productSeriesEnqueueUnmatchedStateTx(ctx, tx, reason) + if err != nil { + return stats, err + } + stats.Changed += retryCount return stats, tx.Commit() } @@ -687,6 +692,63 @@ ON CONFLICT (row_key) WHERE status IN ('pending','processing') DO NOTHING return 1, nil } +func productSeriesEnqueueUnmatchedStateTx(ctx context.Context, tx *sql.Tx, reason string) (int, error) { + rows, err := tx.QueryContext(ctx, ` +WITH candidates AS ( + SELECT + s.row_key, + s.product_code, + s.color_code, + s.dim3_code + FROM mk_product_series_stock_state s + LEFT JOIN mmitem m + ON BTRIM(m.code) = BTRIM(s.product_code) + LEFT JOIN mk_dim_token_map d1 + ON d1.dim_column='dimval1' + AND BTRIM(d1.token) = BTRIM(s.color_code) + LEFT JOIN mk_dim_token_map d3 + ON d3.dim_column='dimval3' + AND BTRIM(d3.token) = BTRIM(s.dim3_code) + LEFT JOIN zbggseri z + ON z.mmitem_id = m.id + AND z.dim1 = d1.dim_id + AND ( + (NULLIF(BTRIM(s.dim3_code), '') IS NULL AND z.dim3 IS NULL) + OR z.dim3 = d3.dim_id + ) + WHERE s.total_qty > 0 + AND s.last_seen_at > now() - interval '30 days' + AND ( + m.id IS NULL + OR d1.dim_id IS NULL + OR (NULLIF(BTRIM(s.dim3_code), '') IS NOT NULL AND d3.dim_id IS NULL) + OR z.id IS NULL + ) + ORDER BY s.updated_at DESC + LIMIT 2000 +), +inserted AS ( + INSERT INTO mk_product_series_recalc_queue (row_key, product_code, color_code, dim3_code, reason, status, attempts, available_at, queued_at, last_error, created_at, updated_at) + SELECT row_key, product_code, color_code, dim3_code, $1, 'pending', 0, now(), now(), '', now(), now() + FROM candidates + ON CONFLICT (row_key) WHERE status IN ('pending','processing') DO NOTHING + RETURNING id +) +SELECT COUNT(*) FROM inserted +`, "unmatched_retry_"+strings.TrimSpace(reason)) + if err != nil { + return 0, err + } + defer rows.Close() + var count int + if rows.Next() { + if err := rows.Scan(&count); err != nil { + return 0, err + } + } + return count, rows.Err() +} + func productSeriesMarkQueueDone(ctx context.Context, pg *sql.DB, id int64) error { _, err := pg.ExecContext(ctx, ` UPDATE mk_product_series_recalc_queue diff --git a/ui/.quasar/prod-spa/app.js b/ui/.quasar/prod-spa/app.js new file mode 100644 index 0000000..caeaac1 --- /dev/null +++ b/ui/.quasar/prod-spa/app.js @@ -0,0 +1,75 @@ +/* eslint-disable */ +/** + * THIS FILE IS GENERATED AUTOMATICALLY. + * DO NOT EDIT. + * + * You are probably looking on adding startup/initialization code. + * Use "quasar new boot " and add it there. + * One boot file per concern. Then reference the file(s) in quasar.config file > boot: + * boot: ['file', ...] // do not add ".js" extension to it. + * + * Boot files are your "main.js" + **/ + + + + + +import { Quasar } from 'quasar' +import { markRaw } from 'vue' +import RootComponent from 'app/src/App.vue' + +import createStore from 'app/src/stores/index' +import createRouter from 'app/src/router/index' + + + + + +export default async function (createAppFn, quasarUserOptions) { + + + // Create the app instance. + // Here we inject into it the Quasar UI, the router & possibly the store. + const app = createAppFn(RootComponent) + + + + app.use(Quasar, quasarUserOptions) + + + + + const store = typeof createStore === 'function' + ? await createStore({}) + : createStore + + + app.use(store) + + + + + + const router = markRaw( + typeof createRouter === 'function' + ? await createRouter({store}) + : createRouter + ) + + + // make router instance available in store + + store.use(({ store }) => { store.router = router }) + + + + // Expose the app, the router and the store. + // Note that we are not mounting the app here, since bootstrapping will be + // different depending on whether we are in a browser or on the server. + return { + app, + store, + router + } +} diff --git a/ui/.quasar/prod-spa/client-entry.js b/ui/.quasar/prod-spa/client-entry.js new file mode 100644 index 0000000..5223e2b --- /dev/null +++ b/ui/.quasar/prod-spa/client-entry.js @@ -0,0 +1,158 @@ +/* eslint-disable */ +/** + * THIS FILE IS GENERATED AUTOMATICALLY. + * DO NOT EDIT. + * + * You are probably looking on adding startup/initialization code. + * Use "quasar new boot " and add it there. + * One boot file per concern. Then reference the file(s) in quasar.config file > boot: + * boot: ['file', ...] // do not add ".js" extension to it. + * + * Boot files are your "main.js" + **/ + + +import { createApp } from 'vue' + + + + + + + +import '@quasar/extras/roboto-font/roboto-font.css' + +import '@quasar/extras/material-icons/material-icons.css' + + + + +// We load Quasar stylesheet file +import 'quasar/dist/quasar.sass' + + + + +import 'src/css/app.css' + + +import createQuasarApp from './app.js' +import quasarUserOptions from './quasar-user-options.js' + + + + + + + + +const publicPath = `/` + + +async function start ({ + app, + router + , store +}, bootFiles) { + + let hasRedirected = false + const getRedirectUrl = url => { + try { return router.resolve(url).href } + catch (err) {} + + return Object(url) === url + ? null + : url + } + const redirect = url => { + hasRedirected = true + + if (typeof url === 'string' && /^https?:\/\//.test(url)) { + window.location.href = url + return + } + + const href = getRedirectUrl(url) + + // continue if we didn't fail to resolve the url + if (href !== null) { + window.location.href = href + window.location.reload() + } + } + + const urlPath = window.location.href.replace(window.location.origin, '') + + for (let i = 0; hasRedirected === false && i < bootFiles.length; i++) { + try { + await bootFiles[i]({ + app, + router, + store, + ssrContext: null, + redirect, + urlPath, + publicPath + }) + } + catch (err) { + if (err && err.url) { + redirect(err.url) + return + } + + console.error('[Quasar] boot error:', err) + return + } + } + + if (hasRedirected === true) return + + + app.use(router) + + + + + + + app.mount('#q-app') + + + +} + +createQuasarApp(createApp, quasarUserOptions) + + .then(app => { + // eventually remove this when Cordova/Capacitor/Electron support becomes old + const [ method, mapFn ] = Promise.allSettled !== void 0 + ? [ + 'allSettled', + bootFiles => bootFiles.map(result => { + if (result.status === 'rejected') { + console.error('[Quasar] boot error:', result.reason) + return + } + return result.value.default + }) + ] + : [ + 'all', + bootFiles => bootFiles.map(entry => entry.default) + ] + + return Promise[ method ]([ + + import(/* webpackMode: "eager" */ 'boot/dayjs'), + + import(/* webpackMode: "eager" */ 'boot/locale'), + + import(/* webpackMode: "eager" */ 'boot/resizeObserverGuard') + + ]).then(bootFiles => { + const boot = mapFn(bootFiles).filter(entry => typeof entry === 'function') + start(app, boot) + }) + }) + diff --git a/ui/.quasar/prod-spa/client-prefetch.js b/ui/.quasar/prod-spa/client-prefetch.js new file mode 100644 index 0000000..9bbe3c5 --- /dev/null +++ b/ui/.quasar/prod-spa/client-prefetch.js @@ -0,0 +1,116 @@ +/* eslint-disable */ +/** + * THIS FILE IS GENERATED AUTOMATICALLY. + * DO NOT EDIT. + * + * You are probably looking on adding startup/initialization code. + * Use "quasar new boot " and add it there. + * One boot file per concern. Then reference the file(s) in quasar.config file > boot: + * boot: ['file', ...] // do not add ".js" extension to it. + * + * Boot files are your "main.js" + **/ + + + +import App from 'app/src/App.vue' +let appPrefetch = typeof App.preFetch === 'function' + ? App.preFetch + : ( + // Class components return the component options (and the preFetch hook) inside __c property + App.__c !== void 0 && typeof App.__c.preFetch === 'function' + ? App.__c.preFetch + : false + ) + + +function getMatchedComponents (to, router) { + const route = to + ? (to.matched ? to : router.resolve(to).route) + : router.currentRoute.value + + if (!route) { return [] } + + const matched = route.matched.filter(m => m.components !== void 0) + + if (matched.length === 0) { return [] } + + return Array.prototype.concat.apply([], matched.map(m => { + return Object.keys(m.components).map(key => { + const comp = m.components[key] + return { + path: m.path, + c: comp + } + }) + })) +} + +export function addPreFetchHooks ({ router, store, publicPath }) { + // Add router hook for handling preFetch. + // Doing it after initial route is resolved so that we don't double-fetch + // the data that we already have. Using router.beforeResolve() so that all + // async components are resolved. + router.beforeResolve((to, from, next) => { + const + urlPath = window.location.href.replace(window.location.origin, ''), + matched = getMatchedComponents(to, router), + prevMatched = getMatchedComponents(from, router) + + let diffed = false + const preFetchList = matched + .filter((m, i) => { + return diffed || (diffed = ( + !prevMatched[i] || + prevMatched[i].c !== m.c || + m.path.indexOf('/:') > -1 // does it has params? + )) + }) + .filter(m => m.c !== void 0 && ( + typeof m.c.preFetch === 'function' + // Class components return the component options (and the preFetch hook) inside __c property + || (m.c.__c !== void 0 && typeof m.c.__c.preFetch === 'function') + )) + .map(m => m.c.__c !== void 0 ? m.c.__c.preFetch : m.c.preFetch) + + + if (appPrefetch !== false) { + preFetchList.unshift(appPrefetch) + appPrefetch = false + } + + + if (preFetchList.length === 0) { + return next() + } + + let hasRedirected = false + const redirect = url => { + hasRedirected = true + next(url) + } + const proceed = () => { + + if (hasRedirected === false) { next() } + } + + + + preFetchList.reduce( + (promise, preFetch) => promise.then(() => hasRedirected === false && preFetch({ + store, + currentRoute: to, + previousRoute: from, + redirect, + urlPath, + publicPath + })), + Promise.resolve() + ) + .then(proceed) + .catch(e => { + console.error(e) + proceed() + }) + }) +} diff --git a/ui/.quasar/prod-spa/quasar-user-options.js b/ui/.quasar/prod-spa/quasar-user-options.js new file mode 100644 index 0000000..ac1dae3 --- /dev/null +++ b/ui/.quasar/prod-spa/quasar-user-options.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +/** + * THIS FILE IS GENERATED AUTOMATICALLY. + * DO NOT EDIT. + * + * You are probably looking on adding startup/initialization code. + * Use "quasar new boot " and add it there. + * One boot file per concern. Then reference the file(s) in quasar.config file > boot: + * boot: ['file', ...] // do not add ".js" extension to it. + * + * Boot files are your "main.js" + **/ + +import lang from 'quasar/lang/tr.js' + + + +import {Loading,Dialog,Notify} from 'quasar' + + + +export default { config: {"notify":{"position":"top","timeout":2500}},lang,plugins: {Loading,Dialog,Notify} } +