waibu-db 2.15.0 → 2.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,29 +1,29 @@
1
1
  <c:grid-row gutter="2">
2
2
  <c:grid-col col="6-lg">
3
- <% if (!_.get(schema, 'view.control.wdbBtnBack.disabled')) { %>
4
- <c:wdb-btn-back href="<%= _.get(schema, 'view.control.wdbBtnBack.href', 'undefined') %>" />
3
+ <% if (!_getSetting('waibuDb:/control/wdbBtnBack/disabled')) { %>
4
+ <c:wdb-btn-back href="<%= _getSetting('waibuDb:/control/wdbBtnBack/href', 'undefined') %>" />
5
5
  <% } %>
6
6
  <% if (schema.disabled.includes('remove') && schema.disabled.includes('update')) { %>
7
- <% if (!_.get(schema, 'view.control.wdbBtnExport.disabled')) { %>
7
+ <% if (!_getSetting('waibuDb:/control/wdbBtnExport/disabled')) { %>
8
8
  <c:wdb-btn-export selector="#main-form" handler="details" launch-margin="start-1" />
9
9
  <% } %>
10
10
  <% } else { %>
11
11
  <c:btn-group margin="start-1">
12
- <% if (!_.get(schema, 'view.control.wdbBtnEdit.disabled')) { %>
13
- <c:wdb-btn-edit href="<%= _.get(schema, 'view.control.wdbBtnEdit.href', 'undefined') %>" />
12
+ <% if (!_getSetting('waibuDb:/control/wdbBtnEdit/disabled')) { %>
13
+ <c:wdb-btn-edit href="<%= _getSetting('waibuDb:/control/wdbBtnEdit/href', 'undefined') %>" />
14
14
  <% } %>
15
- <% if (!_.get(schema, 'view.control.wdbBtnClone.disabled')) { %>
16
- <c:wdb-btn-clone href="<%= _.get(schema, 'view.control.wdbBtnClone.href', 'undefined') %>" />
15
+ <% if (!_getSetting('waibuDb:/control/wdbBtnClone/disabled')) { %>
16
+ <c:wdb-btn-clone href="<%= _getSetting('waibuDb:/control/wdbBtnClone/href', 'undefined') %>" />
17
17
  <% } %>
18
- <% if (!_.get(schema, 'view.control.wdbBtnExport.disabled')) { %>
18
+ <% if (!_getSetting('waibuDb:/control/wdbBtnExport/disabled')) { %>
19
19
  <c:wdb-btn-export selector="#main-form" handler="details" launch-on-end no-save/>
20
20
  <% } %>
21
21
  </c:btn-group>
22
22
  <% } %>
23
23
  </c:grid-col>
24
24
  <c:grid-col col="6-lg" flex="justify-content:end-lg align-items:center">
25
- <% if (!_.get(schema, 'view.control.wdbBtnDelete.disabled')) { %>
26
- <c:wdb-btn-delete href="<%= _.get(schema, 'view.control.wdbBtnDelete.href', 'undefined') %>" />
25
+ <% if (!_getSetting('waibuDb:/control/wdbBtnDelete/disabled')) { %>
26
+ <c:wdb-btn-delete href="<%= _getSetting('waibuDb:/control/wdbBtnDelete/href', 'undefined') %>" />
27
27
  <% } %>
28
28
  </c:grid-col>
29
29
  </c:grid-row>
@@ -1,28 +1,28 @@
1
1
  <c:grid-row gutter="2">
2
2
  <c:grid-col col="6-lg">
3
- <% if (!_.get(schema, 'view.control.wdbBtnBack.disabled')) { %>
4
- <c:wdb-btn-back href="<%= _.get(schema, 'view.control.wdbBtnBack.href', 'undefined') %>"/>
3
+ <% if (!_getSetting('waibuDb:/control/wdbBtnBack/disabled')) { %>
4
+ <c:wdb-btn-back href="<%= _getSetting('waibuDb:/control/wdbBtnBack/href', 'undefined') %>"/>
5
5
  <% } %>
6
6
  <c:btn-group margin="start-1">
7
- <% if (!_.get(schema, 'view.control.wdbBtnDetails.disabled')) { %>
8
- <c:wdb-btn-details href="<%= _.get(schema, 'view.control.wdbBtnDetails.href', 'undefined') %>" />
7
+ <% if (!_getSetting('waibuDb:/control/wdbBtnDetails/disabled')) { %>
8
+ <c:wdb-btn-details href="<%= _getSetting('waibuDb:/control/wdbBtnDetails/href', 'undefined') %>" />
9
9
  <% } %>
10
- <% if (!_.get(schema, 'view.control.wdbBtnClone.disabled')) { %>
11
- <c:wdb-btn-clone href="<%= _.get(schema, 'view.control.wdbBtnClone.href', 'undefined') %>" />
10
+ <% if (!_getSetting('waibuDb:/control/wdbBtnClone/disabled')) { %>
11
+ <c:wdb-btn-clone href="<%= _getSetting('waibuDb:/control/wdbBtnClone/href', 'undefined') %>" />
12
12
  <% } %>
13
- <% if (!_.get(schema, 'view.control.wdbBtnExport.disabled')) { %>
13
+ <% if (!_getSetting('waibuDb:/control/wdbBtnExport/disabled')) { %>
14
14
  <c:wdb-btn-export selector="#main-form" handler="edit" launch-on-end no-save />
15
15
  <% } %>
16
16
  </c:btn-group>
17
17
  </c:grid-col>
18
18
  <c:grid-col col="6-lg" flex="justify-content:end-lg align-items:center">
19
- <% if (!_.get(schema, 'view.control.wdbBtnDelete.disabled')) { %>
20
- <c:wdb-btn-delete href="<%= _.get(schema, 'view.control.wdbBtnDelete.href', 'undefined') %>"/>
19
+ <% if (!_getSetting('waibuDb:/control/wdbBtnDelete/disabled')) { %>
20
+ <c:wdb-btn-delete href="<%= _getSetting('waibuDb:/control/wdbBtnDelete/href', 'undefined') %>"/>
21
21
  <% } %>
22
- <% if (!_.get(schema, 'view.control.wdbBtnReset.disabled')) { %>
22
+ <% if (!_getSetting('waibuDb:/control/wdbBtnReset/disabled')) { %>
23
23
  <c:btn type="reset" color="secondary" t:content="reset" margin="start-2"/>
24
24
  <% } %>
25
- <% if (!_.get(schema, 'view.control.wdbBtnSubmit.disabled')) { %>
25
+ <% if (!_getSetting('waibuDb:/control/wdbBtnSubmit/disabled')) { %>
26
26
  <c:btn type="submit" color="primary" t:content="submit" margin="start-2"/>
27
27
  <% } %>
28
28
  </c:grid-col>
@@ -1,8 +1,8 @@
1
1
  <c:grid-row gutter="3">
2
- <c:grid-col col="4-lg">
2
+ <c:grid-col col="5-lg">
3
3
  <c:wdb-query />
4
4
  </c:grid-col>
5
- <c:grid-col col="8-lg" flex="justify-content:end-lg">
5
+ <c:grid-col col="7-lg" flex="justify-content:end-lg">
6
6
  <c:btn-group margin="end-2">
7
7
  <c:wdb-btn-add text="nowrap"/>
8
8
  <c:wdb-btn-details on-list text="nowrap"/>
@@ -6,6 +6,7 @@ async function wdbBase () {
6
6
  const { get } = this.app.lib._
7
7
  this.schema = get(this, 'component.locals.schema', {})
8
8
  this.formData = get(this, 'component.locals.form', {})
9
+ this.oldData = get(this, 'component.locals.oldData', {})
9
10
  this.model = getModel(this.schema.name, true)
10
11
  }
11
12
 
@@ -37,13 +38,12 @@ async function wdbBase () {
37
38
  return refName
38
39
  }
39
40
 
40
- getSetting = (key, defaultValue) => {
41
- const { req } = this.component
41
+ getSetting = (key, defValue) => {
42
42
  const { get, camelCase } = this.app.lib._
43
43
  const widgetName = camelCase(this.constructor.name)
44
44
  key = key.replaceAll('{self}', widgetName)
45
- const config = req.getSetting(`${this.plugin.ns}:${key}`, defaultValue)
46
- return get(this.schema, `view.${key}`, config)
45
+ const cfg = this.app.waibu.getSetting(`${this.plugin.ns}:${key}`, { defValue, req: this.component.req })
46
+ return get(this.schema, `view.${key}`, cfg)
47
47
  }
48
48
  }
49
49
  }
@@ -67,16 +67,13 @@ async function btnExport () {
67
67
  let items = []
68
68
  let checker = false
69
69
  const keys = []
70
- const types = []
71
70
  let els = document.querySelectorAll(selector + ' thead th')
72
71
  for (const el of els) {
73
72
  keys.push(this.options.includes('fkey') ? el.innerText : el.dataset.key)
74
- types.push(el.dataset.type)
75
73
  }
76
74
  if (_.isEmpty(keys[0])) {
77
75
  checker = true
78
76
  keys.shift()
79
- types.shift()
80
77
  }
81
78
  els = document.querySelectorAll(selector + ' tbody tr')
82
79
  for (const el of els) {
@@ -85,12 +82,7 @@ async function btnExport () {
85
82
  i = i + ''
86
83
  if (i === '0' && checker) return undefined
87
84
  if (this.options.includes('fvalue')) data.push(v.innerText)
88
- else {
89
- const type = types[parseInt(i)]
90
- let val = wmpa.parseValue(v.dataset.value, type)
91
- if (['datetime', 'date', 'time'].includes(type)) val = val.toISOString()
92
- data.push(val)
93
- }
85
+ else data.push(wmpa.parseValue(v.dataset.value, v.dataset.value))
94
86
  })
95
87
  const item = {}
96
88
  for (const i in keys) {
@@ -1,5 +1,55 @@
1
1
  import wdbBase from '../wdb-base.js'
2
2
 
3
+ async function handleRo (attr = {}, prop = {}, widget = {}) {
4
+ const { get, camelCase, isEmpty, isString } = this.app.lib._
5
+ const { callHandler } = this.app.bajo
6
+ const { escape } = this.app.waibu
7
+ const { req } = this.component
8
+ const dataValue = get(this.formData, `_orig.${prop.name}`, prop.dataValue ?? '')
9
+ let value = get(this.oldData, prop.name, get(this.formData, prop.name, prop.value ?? ''))
10
+ const format = get(this.schema, `view.format.${prop.name}`)
11
+ const formatValue = get(this.schema, `view.formatValue.${prop.name}`)
12
+ const labelField = get(this.schema, `view.widget.${prop.name}.attr.labelField`)
13
+ if (formatValue) value = await formatValue.call(this, value, this.formData, { req })
14
+ else if (prop.ref) {
15
+ value = this.getRefValue({ field: prop.name, labelField, refName: this.getRefName(prop.name) })
16
+ if (format && !isEmpty(value)) attr.href = await format.call(this, value, this.formData, { linkOnly: true })
17
+ } else if (prop.values) {
18
+ const values = isString(prop.values) ? (await callHandler(prop.values)) : prop.values
19
+ value = values.find(v => v.value === dataValue)
20
+ if (value) {
21
+ const key = camelCase(`${prop.name} ${value.text}`)
22
+ value = req.te(key) ? req.t(key) : value.text
23
+ }
24
+ } else if (format && !isEmpty(value)) value = await format.call(this, value, this.formData)
25
+ attr.dataValue = escape(dataValue)
26
+ attr.value = escape(value)
27
+ attr.dataType = prop.type
28
+
29
+ if (['object', 'array', 'text'].includes(prop.type)) {
30
+ attr.style = 'min-height: 100px'
31
+ attr.readonly = true
32
+ return await this.component.buildTag({ tag: 'formTextarea', attr, html: value })
33
+ }
34
+ return await this.component.buildTag({ tag: 'formPlaintext', attr, selfCosing: true, noEscape: true })
35
+ }
36
+
37
+ async function handleRw (attr = {}, prop = {}, widget = {}) {
38
+ const { get, has, isPlainObject, isArray } = this.app.lib._
39
+ const { escape } = this.app.waibu
40
+ const { stringifyAttribs } = this.app.waibuMpa
41
+ if (has(attr, 'name') && !has(attr, 'value')) {
42
+ attr.dataType = attr.dataType ?? prop.type
43
+ attr.dataValue = get(this, `formData.${attr.name}`)
44
+ if (isPlainObject(attr.dataValue) || isArray(attr.dataValue)) attr.dataValue = JSON.stringify(attr.dataValue)
45
+ attr.dataValue = escape(attr.dataValue)
46
+ attr.value = widget.component === 'form-plaintext' ? get(this, `oldData.${attr.name}`, attr.dataValue) : attr.dataValue
47
+ }
48
+
49
+ const cmp = prop.ref ? 'wdb-lookup-select' : widget.component
50
+ return `<c:${cmp} ${stringifyAttribs(attr)} data-type="${prop.type}" />`
51
+ }
52
+
3
53
  async function form () {
4
54
  const WdbBase = await wdbBase.call(this)
5
55
 
@@ -7,8 +57,6 @@ async function form () {
7
57
  build = async () => {
8
58
  const { get, find, filter, forOwn, isEmpty } = this.app.lib._
9
59
  const { base64JsonEncode } = this.app.waibu
10
- const { req } = this.component
11
- const data = get(this, 'component.locals.form', {})
12
60
  const body = []
13
61
  const xModels = get(this.schema, 'view.x.model', [])
14
62
  const xOns = get(this.schema, 'view.x.on', [])
@@ -17,36 +65,30 @@ async function form () {
17
65
  if (fields.length === 0) continue
18
66
  body.push(`<c:fieldset ${this.schema.view.card === false ? '' : 'card'} ${l.name[0] !== '_' ? ('t:legend="' + l.name + '"') : ''} grid-gutter="2">`)
19
67
  for (const f of fields) {
20
- const w = this.schema.view.widget[f]
68
+ const widget = this.schema.view.widget[f]
21
69
  let prop = find(this.schema.properties, { name: f })
22
70
  if (!prop) prop = find(this.schema.view.calcFields, { name: f })
23
71
  if (!prop) continue
24
- const attr = [`x-ref="${w.name}"`]
25
- if (xModels.includes(w.name)) attr.push(`x-model="${w.name}"`)
26
- forOwn(w.attr, (v, k) => {
27
- if (v === true) attr.push(k)
28
- else attr.push(`${k}="${v}"`)
72
+ const attr = {
73
+ 'x-ref': widget.name,
74
+ labelFloating: true,
75
+ name: widget.name
76
+ }
77
+ if (xModels.includes(widget.name)) attr['x-model'] = widget.name
78
+ forOwn(widget.attr, (v, k) => {
79
+ if (v === true) attr[k] = true
80
+ else attr[k] = v
29
81
  })
30
- const xon = filter(xOns, { field: w.name })
82
+ attr.label = this.component.req.t(attr.label)
83
+ const xon = filter(xOns, { field: widget.name })
31
84
  for (const o of xon) {
32
- attr.push(`@${o.bind}="${o.handler}"`)
85
+ attr[`@${o.bind}`] = o.handler
33
86
  }
34
- if (w.componentOpts) attr.push(`c-opts="${base64JsonEncode(w.componentOpts)}"`)
35
- const attributes = `${w.attr.label ? ('t:label="' + w.attr.label + '"') : ''} label-floating name="${w.name}" ${attr.join(' ')}`
36
- if (w.component === 'form-plaintext' || this.params.attr.method !== 'POST') {
37
- let value
38
- let link
39
- if (this.schema.view.formatValue[f]) value = await this.schema.view.formatValue[f].call(this, data[f], data, { req })
40
- else if (prop.ref) {
41
- value = this.getRefValue({ field: f, labelField: w.attr.labelField, refName: this.getRefName(f) })
42
- const format = get(this.schema, `view.format.${f}`)
43
- if (format && !isEmpty(value)) link = await format.call(this.model, value, data, { linkOnly: true })
44
- }
45
- body.push(`<c:${w.component} ${attributes} data-type="${prop.type}" ${value ? `value="${value}"` : ''} ${link ? `href="${link}"` : ''} />`)
46
- } else if (prop.ref) {
47
- body.push(`<c:wdb-lookup-select ${attributes} />`)
87
+ if (widget.componentOpts) attr['c-opts'] = base64JsonEncode(widget.componentOpts)
88
+ if (widget.component === 'form-plaintext' || this.params.attr.method !== 'POST') {
89
+ body.push(await handleRo.call(this, attr, prop, widget))
48
90
  } else {
49
- body.push(`<c:${w.component} ${attributes} data-type="${prop.type}" />`)
91
+ body.push(await handleRw.call(this, attr, prop, widget))
50
92
  }
51
93
  }
52
94
  body.push('</c:fieldset>')
@@ -63,7 +63,7 @@ async function query () {
63
63
  const container = this.params.attr.modal ? 'modal' : 'drawer'
64
64
  const scanables = (this.model ? this.model.scanables : []).map(item => req.t(`field.${item}`))
65
65
  let placeholder = this.params.attr.placeholder
66
- if (!placeholder) placeholder = scanables.length > 0 ? req.t('queryHint%s', join(scanables, { lastSeparator: 'or' })) : req.t('query')
66
+ if (!placeholder) placeholder = scanables.length > 0 ? req.t('queryHint%s', join(scanables, { separator: ', ', lastSeparator: 'or' })) : req.t('query')
67
67
  this.params.html = await this.component.buildSentence(`
68
68
  <c:form-input type="search" placeholder="${placeholder}" id="${id}" x-data="{ query: '' }" x-init="
69
69
  const url = new URL(window.location.href)
@@ -26,7 +26,7 @@ async function table () {
26
26
  const { req } = this.component
27
27
  const { escape, attrToArray } = this.app.waibu
28
28
  const { groupAttrs } = this.app.waibuMpa
29
- const { get, omit, set, find, isEmpty, without, merge } = this.app.lib._
29
+ const { get, omit, set, find, isEmpty, without, merge, camelCase } = this.app.lib._
30
30
  const group = groupAttrs(this.params.attr, ['body', 'head', 'foot'])
31
31
  this.params.attr = group._
32
32
  const prettyUrl = this.params.attr.prettyUrl
@@ -38,6 +38,7 @@ async function table () {
38
38
  // collect prop.values for later use
39
39
  for (const prop of schema.properties) {
40
40
  if (typeof prop.values === 'string') this.propValues[prop.name] = await callHandler(prop.values)
41
+ else if (prop.values) this.propValues[prop.name] = prop.values
41
42
  }
42
43
  if (count === 0 || data.length === 0) {
43
44
  const alert = '<c:alert color="warning" t:content="noRecordFound" margin="top-4"/>'
@@ -133,12 +134,10 @@ async function table () {
133
134
  let prop = find(schema.properties, { name: f })
134
135
  if (!prop) prop = find(schema.view.calcFields, { name: f })
135
136
  if (!prop) continue
136
- let dataValue = d._orig[f] ?? ''
137
- if (!isEmpty(dataValue)) {
138
- if (['datetime'].includes(prop.type)) dataValue = escape(dataValue.toISOString())
139
- if (['string', 'text'].includes(prop.type)) dataValue = escape(dataValue)
140
- if (['array', 'object'].includes(prop.type)) dataValue = escape(JSON.stringify(d[f]))
141
- }
137
+ let dataValue = d._orig[f]
138
+ if (['datetime'].includes(prop.type)) dataValue = escape(dataValue.toISOString())
139
+ else if (['string', 'text'].includes(prop.type)) dataValue = escape(dataValue)
140
+ else if (['array', 'object'].includes(prop.type)) dataValue = escape(JSON.stringify(d[f]))
142
141
  const refName = get(schema, `view.widget.${f}.attr.refName`)
143
142
  let value = this.getRefValue({ field: f, data: d, refName }) ?? d[f]
144
143
  const formatValue = get(schema, `view.formatValue.${f}`)
@@ -156,11 +155,18 @@ async function table () {
156
155
  else attr.text = noWrap
157
156
  const lookup = get(schema, `view.lookup.${f}`)
158
157
  if (lookup) {
159
- const item = find(lookup.values, set({}, lookup.id ?? 'id', d[f]))
158
+ const item = find(lookup.values, set({}, lookup.id ?? 'id', dataValue))
160
159
  if (item) value = req.t(item[lookup.field ?? 'name'])
161
160
  }
162
161
  const format = get(schema, `view.format.${f}`)
163
162
  if (format) value = await format.call(this, value, d, { params: this.params, req })
163
+ if (this.propValues[f]) {
164
+ const item = find(this.propValues[f], { value: dataValue })
165
+ if (item) {
166
+ const key = camelCase(`${f} ${item.text}`)
167
+ value = req.te(key) ? req.t(key) : item.text
168
+ }
169
+ }
164
170
  const line = await this.component.buildTag({ tag: 'td', attr, html: value })
165
171
  lines.push(line)
166
172
  }
@@ -14,8 +14,8 @@ async function addHandler ({ req, reply, model, params = {}, template, addOnsHan
14
14
  delete req.query.query
15
15
  let def = {}
16
16
  if (req.method === 'GET' && req.query.mode === 'clone' && req.query.id) {
17
- const resp = await getRecord({ model, req, id: req.query.id, options: { fields: map(schema.properties, 'name') } })
18
- def = omit(resp.data, ['id', 'createdAt', 'updatedAt'])
17
+ const resp = await getRecord({ model, req, id: req.query.id, options: { fields: map(schema.properties, 'name'), ...opts } })
18
+ def = omit(resp.data._orig, ['id', 'createdAt', 'updatedAt'])
19
19
  }
20
20
  let form = defaultsDeep(req.body, def)
21
21
  let error
@@ -7,7 +7,7 @@ async function editHandler ({ req, reply, model, id, params = {}, template, addO
7
7
  const { buildUrl } = this.app.waibuMpa
8
8
  const { fs } = this.app.lib
9
9
  const { defaultsDeep } = this.app.lib.aneka
10
- const { merge, isEmpty, omit } = this.app.lib._
10
+ const { merge, isEmpty, omit, cloneDeep, isArray, isPlainObject } = this.app.lib._
11
11
  const opts = merge({}, options.modelOpts)
12
12
  let error
13
13
  let resp
@@ -22,7 +22,12 @@ async function editHandler ({ req, reply, model, id, params = {}, template, addO
22
22
  delete req.query.query
23
23
  const old = await getRecord({ model, req, id, options: opts })
24
24
  if (isEmpty(old.data)) return await reply.view(notFoundTpl, params)
25
- form = defaultsDeep(req.body, old.data)
25
+ opts._data = old
26
+ const def = cloneDeep(old.data._orig)
27
+ for (const k in def) {
28
+ if (isArray(def[k]) || isPlainObject(def[k])) def[k] = JSON.stringify(def[k])
29
+ }
30
+ form = defaultsDeep(req.body, def)
26
31
  if (req.method !== 'GET') {
27
32
  form = omit(form, ['_action', '_value'])
28
33
  try {
@@ -49,7 +54,7 @@ async function editHandler ({ req, reply, model, id, params = {}, template, addO
49
54
  }
50
55
  const addOns = addOnsHandler ? await addOnsHandler.call(this.app[req.ns], { req, reply, params, data: resp, schema, error, options }) : undefined
51
56
  const attachments = await attachmentHandler.call(this, { schema, id, options })
52
- merge(params, { form, schema, error, addOns, attachments })
57
+ merge(params, { oldData: old.data, form, schema, error, addOns, attachments })
53
58
  if (schema.template) template = schema.template
54
59
  if (schema.layout) params.page.layout = schema.layout
55
60
  return await reply.view(template, params)
@@ -1,11 +1,11 @@
1
1
  import { prepCrud, processHandler } from '../util.js'
2
2
 
3
- async function createRecord ({ model, req, reply, body, options = {}, transaction } = {}) {
4
- const { model: mdl, input, opts, attachment, stats, mimeType } = await prepCrud.call(this, { model, req, reply, body, options, args: ['model'], transaction })
3
+ async function createRecord ({ model, req, reply, options = {}, transaction } = {}) {
4
+ const { model: mdl, opts, attachment, stats, mimeType } = await prepCrud.call(this, { model, req, reply, options, args: ['model'], transaction })
5
5
 
6
6
  async function handler (trx) {
7
7
  if (opts.trx === true) opts.trx = trx
8
- const ret = await mdl.createRecord(input, opts)
8
+ const ret = await mdl.createRecord(req.body, { ...opts, partial: true, strict: true })
9
9
  if (attachment) ret.data._attachment = await mdl.findAttachment(ret.data.id, { stats, mimeType })
10
10
  return ret
11
11
  }
@@ -1,14 +1,16 @@
1
1
  import { prepCrud, getOneRecord, processHandler } from '../util.js'
2
2
 
3
3
  async function updateRecord ({ model, req, reply, id, body, options = {}, transaction } = {}) {
4
- const { model: mdl, filter, input, opts, recId, attachment, stats, mimeType } = await prepCrud.call(this, { model, req, reply, body, id, options, args: ['model', 'id'], transaction })
4
+ const { model: mdl, filter, opts, recId, attachment, stats, mimeType } = await prepCrud.call(this, { model, req, reply, id, options, args: ['model', 'id'], transaction })
5
5
  const me = this
6
6
 
7
7
  async function handler (trx) {
8
8
  if (opts.trx === true) opts.trx = trx
9
- const resp = await getOneRecord.call(me, mdl, recId, filter, opts)
10
- opts._data = resp.data
11
- const ret = await mdl.updateRecord(recId, input, opts)
9
+ if (!opts._data) {
10
+ const resp = await getOneRecord.call(me, mdl, recId, filter, opts)
11
+ opts._data = resp.data
12
+ }
13
+ const ret = await mdl.updateRecord(recId, body ?? req.body, { ...opts, partial: true, strict: true })
12
14
  if (attachment) ret.data._attachment = await mdl.findAttachment(id, { stats, mimeType })
13
15
  return ret
14
16
  }
package/lib/util.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export const actions = ['countRecord', 'createAggregate', 'createHistogram', 'createRecord', 'findAllRecord', 'findOneRecord', 'findRecord', 'getRecord', 'removeRecord', 'updateRecord']
2
2
 
3
- export async function prepCrud ({ model, body, id, req, reply, transaction, options = {}, args } = {}) {
3
+ export async function prepCrud ({ model, id, req, reply, transaction, options = {}, args } = {}) {
4
4
  const { isSet } = this.app.lib.aneka
5
5
  const { importModule } = this.app.bajo
6
6
  const { parseFilter } = this.app.waibu
@@ -42,8 +42,6 @@ export async function prepCrud ({ model, body, id, req, reply, transaction, opti
42
42
  model = model ?? pascalCase(params.model)
43
43
  mdl = this.app.dobo.getModel(model)
44
44
  }
45
- const input = await mdl.sanitizeBody({ body: body ?? params.body, partial: true, strict: true })
46
-
47
45
  opts.bboxLatField = req.query[cfgWeb.qsKey.bboxLatField]
48
46
  opts.bboxLngField = req.query[cfgWeb.qsKey.bboxLngField]
49
47
  const filter = parseFilter(req)
@@ -54,7 +52,7 @@ export async function prepCrud ({ model, body, id, req, reply, transaction, opti
54
52
  if (options.limit) filter.limit = options.limit
55
53
  if (options.sort) filter.sort = options.sort
56
54
  if (options.page) filter.page = options.page
57
- return { model: mdl, recId, input, opts, filter, attachment, stats, mimeType }
55
+ return { model: mdl, recId, opts, filter, attachment, stats, mimeType }
58
56
  }
59
57
 
60
58
  export async function getOneRecord (model, id, filter, options) {
@@ -79,7 +77,8 @@ export async function processHandler ({ action, model, handler, options } = {})
79
77
  }
80
78
 
81
79
  try {
82
- return options.trx === true ? (await model.transaction(handler)) : (await handler())
80
+ if (options.trx === true) return await model.transaction(handler)
81
+ return await handler()
83
82
  } catch (err) {
84
83
  if (options.suppressError.includes(action)) return suppressedReturn.call(this, err)
85
84
  throw err
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waibu-db",
3
- "version": "2.15.0",
3
+ "version": "2.16.1",
4
4
  "description": "DB Helper",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/wiki/CHANGES.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-04-16
4
+
5
+ - [2.16.1] Bug fix in ```WdbBtnExport``` widget
6
+ - [2.16.1] Bug fix in ```WdbForm``` widget
7
+ - [2.16.1] Bug fix in ```WdbTable``` widget
8
+ - [2.16.1] Bug fix in ```prepCrud()```
9
+ - [2.16.1] Bug fix in ```createRecord()```
10
+ - [2.16.1] Bug fix in ```updateRecord()```
11
+
12
+ ## 2026-04-13
13
+
14
+ - [2.16.0] Add ```oldData``` propety to ```WdbBase``` widget
15
+ - [2.16.0] Change ```WdbBase.getSetting()``` to also respect setting from ```waibu.getSetting()```
16
+ - [2.16.0] Rewrite ```WdbForm``` widget entirely
17
+ - [2.16.0] Remove redundant call to get old record in ```updateRecord()```
18
+
3
19
  ## 2026-04-11
4
20
 
5
21
  - [2.15.0] Add ```control``` key in config object