Compare commits

..

3033 commits

Author SHA1 Message Date
Lance Edgar 7171c7fb06 fix: use vmodel for confirm password widget input
since previously this did not work at all for butterball (vue3 +
oruga) - although it was never clear why per se..

Refs: #1
2024-11-19 20:53:23 -06:00
Lance Edgar 993f066f2c bump: version 0.22.2 → 0.22.3 2024-11-19 15:45:37 -06:00
Lance Edgar 980031f524 fix: avoid error for trainwreck query when not a customer
when viewing a person's profile, who does not have a customer record,
the trainwreck query can't really return anything since it normally
should be matching on the customer ID
2024-11-18 14:59:50 -06:00
Lance Edgar bcaf0d08bc bump: version 0.22.1 → 0.22.2 2024-11-18 14:08:10 -06:00
Lance Edgar ac439c949b fix: use local/custom enum for continuum operations
since we can't rely on that existing in rattail proper, due to it not
always having sqlalchemy
2024-11-12 19:45:24 -06:00
Lance Edgar 20b3f87dbe fix: add basic master view for Product Costs 2024-11-12 18:30:50 -06:00
Lance Edgar 9e55717041 fix: show continuum operation type when viewing version history 2024-11-12 18:28:41 -06:00
Lance Edgar 772b6610cb fix: always define app attr for ViewSupplement 2024-11-12 18:26:36 -06:00
Lance Edgar 3f27f626df fix: avoid deprecated import 2024-11-10 19:16:45 -06:00
Lance Edgar 29743e70b7 bump: version 0.22.0 → 0.22.1 2024-11-02 16:56:28 -05:00
Lance Edgar 54220601ed fix: fix submit button for running problem report
esp. on Chrome(-based) browsers
2024-11-01 17:47:46 -05:00
Lance Edgar 9a6f8970ae fix: avoid deprecated grid method 2024-10-23 09:46:14 -05:00
Lance Edgar 28f90ad6b5 bump: version 0.21.11 → 0.22.0 2024-10-22 17:09:29 -05:00
Lance Edgar 535317e4f7 fix: avoid deprecated method to suggest username 2024-10-22 15:04:40 -05:00
Lance Edgar 072db39233 feat: add support for new ordering batch from parsed file 2024-10-22 14:26:10 -05:00
Lance Edgar c6365f2631 bump: version 0.21.10 → 0.21.11 2024-10-03 09:05:46 -05:00
Lance Edgar d520f64fee fix: custom method for adding grid action
since for now, we are using custom grid action class
2024-10-03 08:56:52 -05:00
Lance Edgar 2308d2e240 fix: become/stop root should redirect to previous url
for default theme; butterball already did that
2024-09-16 12:55:58 -05:00
Lance Edgar 0b4efae392 bump: version 0.21.9 → 0.21.10 2024-09-15 10:56:01 -05:00
Lance Edgar 0b646d2d18 fix: update project repo links, kallithea -> forgejo 2024-09-14 12:49:37 -05:00
Lance Edgar a4d81a6e3c docs: use markdown for readme file 2024-09-13 18:16:07 -05:00
Lance Edgar 5e742eab17 fix: use better icon for submit button on login page 2024-09-09 08:32:28 -05:00
Lance Edgar b9b8bbd2ea fix: wrap notes text for batch view 2024-08-29 17:18:32 -05:00
Lance Edgar 8df52bf2a2 fix: expose datasync consumer batch size via configure page 2024-08-29 17:01:49 -05:00
Lance Edgar 55f45ae8a0 bump: version 0.21.8 → 0.21.9 2024-08-28 17:38:33 -05:00
Lance Edgar 2219cf8198 fix: render custom attrs in form component tag 2024-08-28 17:38:05 -05:00
Lance Edgar 9be2f63475 bump: version 0.21.7 → 0.21.8 2024-08-28 14:37:40 -05:00
Lance Edgar 812d8d2349 fix: ignore session kwarg for MasterView.make_row_grid() 2024-08-28 14:37:18 -05:00
Lance Edgar 20dcdd8b86 bump: version 0.21.6 → 0.21.7 2024-08-28 14:20:51 -05:00
Lance Edgar bc399182ba fix: avoid error when form value cannot be obtained 2024-08-28 14:20:17 -05:00
Lance Edgar 71d63f6b93 bump: version 0.21.5 → 0.21.6 2024-08-28 09:53:37 -05:00
Lance Edgar 0b6cfaa9c5 fix: avoid error when grid value cannot be obtained 2024-08-28 09:53:14 -05:00
Lance Edgar b81914fbf5 test: fix broken test 2024-08-28 00:35:15 -05:00
Lance Edgar b30f066c41 bump: version 0.21.4 → 0.21.5 2024-08-28 00:30:15 -05:00
Lance Edgar 2e20fc5b75 fix: set empty string for "-new-" file configure option
otherwise the "-new-" option is not properly auto-selected
2024-08-27 13:50:30 -05:00
Lance Edgar ca05e68890 bump: version 0.21.3 → 0.21.4 2024-08-26 16:12:14 -05:00
Lance Edgar 7a9d5772db fix: handle differing email profile keys for appinfo/configure
hopefully this all can improve some day soon..
2024-08-26 16:11:32 -05:00
Lance Edgar dffd951369 bump: version 0.21.2 → 0.21.3 2024-08-26 15:25:56 -05:00
Lance Edgar d67eb2f1cc fix: show non-standard config values for app info configure email
this page is currently showing some basic email sender/recips etc. but
the config keys traditionally used by rattail are different than
wuttjamaican..so for now we must "translate"
2024-08-26 15:24:40 -05:00
Lance Edgar 3a9bf69aa7 bump: version 0.21.1 → 0.21.2 2024-08-26 14:56:15 -05:00
Lance Edgar d1f4c0f150 fix: refactor waterpark base template to use wutta feedback component
although for now we still provide the template and add reply-to
2024-08-26 14:54:45 -05:00
Lance Edgar b7991b5dc6 fix: fix input/output file upload feature for configure pages, per oruga 2024-08-23 16:18:17 -05:00
Lance Edgar c1a2c9cc70 fix: tweak how grid data translates to Vue template context
per wuttaweb changes
2024-08-23 14:14:17 -05:00
Lance Edgar 37f760959d fix: merge filters into main grid template
to better match wuttaweb
2024-08-22 19:58:27 -05:00
Lance Edgar cea3e4b927 fix: add basic wutta view for users
just proving concepts still at this point..nothing reliable
2024-08-22 19:40:21 -05:00
Lance Edgar 29531c83c4 fix: some fixes for wutta people view 2024-08-22 19:21:48 -05:00
Lance Edgar 4c3e3aeb6a fix: various fixes for waterpark theme 2024-08-22 17:09:58 -05:00
Lance Edgar c176d97870 fix: avoid deprecated component form kwarg 2024-08-22 15:54:15 -05:00
Lance Edgar 7d6f75bb05 bump: version 0.21.0 → 0.21.1 2024-08-22 15:33:28 -05:00
Lance Edgar 7b40c527c8 fix: misc. bugfixes per recent changes 2024-08-22 15:31:09 -05:00
Lance Edgar f292850d05 test: fix some tests 2024-08-22 14:59:18 -05:00
Lance Edgar 8d5427e92f bump: version 0.20.1 → 0.21.0 2024-08-22 14:53:59 -05:00
Lance Edgar b8131c8393 fix: change grid reset-view param name to match wuttaweb 2024-08-22 13:49:57 -05:00
Lance Edgar e52a83751e feat: move "most" filtering logic for grid class to wuttaweb
we still define all filters, and the "most important" grid methods for
filtering
2024-08-21 20:16:03 -05:00
Lance Edgar ffa724ef37 fix: move "searchable columns" grid feature to wuttaweb 2024-08-21 15:52:30 -05:00
Lance Edgar 1d00fe994a fix: use wuttaweb to get/render csrf token 2024-08-21 09:44:32 -05:00
Lance Edgar 71abbe06da feat: inherit from wuttaweb templates for home, login pages 2024-08-21 00:49:26 -05:00
Lance Edgar f755460242 feat: inherit from wuttaweb for AppInfoView, appinfo/configure template 2024-08-21 00:49:23 -05:00
Lance Edgar 2ffc067097 fix: inherit from wuttaweb for appinfo/index template
although for now, still must override for some link buttons
2024-08-20 22:27:46 -05:00
Lance Edgar b6a8e508bf fix: prefer wuttaweb config for "home redirect to login" feature 2024-08-20 22:16:01 -05:00
Lance Edgar 1def26a35b feat: add "has output file templates" config option for master view
this is a bit hacky, a quick copy/paste job from the equivalent
feature for input file templates.

i assume this will get cleaned up when moved to wuttaweb..
2024-08-20 19:09:56 -05:00
Lance Edgar 07871188aa fix: fix master/index template rendering for waterpark theme 2024-08-20 17:03:57 -05:00
Lance Edgar c8dc60cb68 fix: fix spacing for navbar logo/title in waterpark theme 2024-08-20 16:49:34 -05:00
Lance Edgar 526c84dfa6 bump: version 0.20.0 → 0.20.1 2024-08-20 16:05:52 -05:00
Lance Edgar 21f90f3f32 fix: fix default filter verbs logic for workorder status 2024-08-20 16:02:35 -05:00
Lance Edgar 83586ef90f bump: version 0.19.3 → 0.20.0 2024-08-20 15:06:09 -05:00
Lance Edgar 59bd58aca7 feat: add new 'waterpark' theme, based on wuttaweb w/ vue2 + buefy
hoping to eventually replace the 'default' view with this one, if all
goes well.  definitely needs more testing and is not exposed as an
option yet, unless configured
2024-08-20 15:03:25 -05:00
Lance Edgar 1ec1eba496 feat: refactor templates to simplify base/page/form structure
to mimic what has been done in wuttaweb
2024-08-19 23:20:59 -05:00
Lance Edgar d29b840343 fix: avoid deprecated reference to app db engine 2024-08-19 14:38:41 -05:00
Lance Edgar b762a0782a bump: version 0.19.2 → 0.19.3 2024-08-19 13:57:36 -05:00
Lance Edgar 15ab0c9592 fix: add pager stats to all grid vue data (fixes view history)
also various other tweaks to modernize
2024-08-19 13:48:18 -05:00
Lance Edgar 41945c5e37 bump: version 0.19.1 → 0.19.2 2024-08-19 12:01:42 -05:00
Lance Edgar f5661fe349 fix: sort on frontend for appinfo package listing grid 2024-08-19 11:56:46 -05:00
Lance Edgar 0eeeb4bd35 fix: prefer attr over key lookup when getting model values
applies to both forms and grids.

the base model class can still handle `obj[key]` but now it is limited
to the column fields only, no association proxies.

so, better to just try `getattr(obj, key)` first and only fall back to
the other if it fails.

unless the obj is clearly a dict in which case try `obj[key]` only
2024-08-19 11:49:52 -05:00
Lance Edgar 1d56a4c0d0 fix: replace all occurrences of component_studly => vue_component 2024-08-19 09:53:10 -05:00
Lance Edgar b642c98d40 bump: version 0.19.0 → 0.19.1 2024-08-19 09:23:55 -05:00
Lance Edgar 0fb3c0f3d2 fix: fix broken user auth for web API app 2024-08-19 09:23:31 -05:00
Lance Edgar b7955a5871 bump: version 0.18.0 → 0.19.0 2024-08-18 19:58:50 -05:00
Lance Edgar 290f8fd51e feat: move multi-column grid sorting logic to wuttaweb
tailbone grid template still duplicates much for Vue, and will until
we can port the filters and anything else remaining..
2024-08-18 19:52:21 -05:00
Lance Edgar ec36df4a34 feat: move single-column grid sorting logic to wuttaweb 2024-08-18 14:05:52 -05:00
Lance Edgar c95e42bf82 fix: fix misc. errors in grid template per wuttaweb 2024-08-18 10:20:48 -05:00
Lance Edgar 5e82fe3946 fix: fix broken permission directives in web api startup 2024-08-18 10:20:48 -05:00
Lance Edgar f4c8176d83 bump: version 0.17.0 → 0.18.0 2024-08-16 22:54:22 -05:00
Lance Edgar 9da2a148c6 feat: move "basic" grid pagination logic to wuttaweb
so far only "simple" pagination is supported by wuttaweb, so basically
the main feature flag, page size, current page.  in this
scenario *all* data is written to client-side JSON and Buefy handles
the actual pagination.

backend pagination coming soon for wuttaweb but for now tailbone still
handles all that.
2024-08-16 18:45:04 -05:00
Lance Edgar 2a0b6da2f9 feat: inherit from wutta base class for Grid 2024-08-16 14:34:50 -05:00
Lance Edgar f7641218cb fix: avoid route error in user view, when using wutta people view
kind of a temporary edge case here, can eventually change it back
2024-08-16 11:56:54 -05:00
Lance Edgar 1b78bd617c feat: inherit most logic from wuttaweb, for GridAction 2024-08-16 11:56:12 -05:00
Lance Edgar 09612b1921 fix: fix some more wutta compat for base template
missed those earlier
2024-08-15 23:46:58 -05:00
Lance Edgar bbd98e7b2f bump: version 0.16.1 → 0.17.0 2024-08-15 23:15:25 -05:00
Lance Edgar da0f6bd5e1 feat: use wuttaweb for get_liburl() logic
thankfully this is already handled and we can remove from tailbone.
although this adds some new cruft as well, to handle auto-migrating
any existing liburl config for apps.

eventually once all apps have migrated to new settings we can remove
the prefix from our calls here but also in wuttaweb signature
2024-08-15 23:12:02 -05:00
Lance Edgar bbc2c584ec bump: version 0.16.0 → 0.16.1 2024-08-15 21:16:53 -05:00
Lance Edgar 7f0c571a44 fix: improve wutta People view a bit
try to behave more like traditional tailbone, for the few things
supported so far.  taking a conservative approach here for now since
probably other things are more pressing.
2024-08-15 21:12:34 -05:00
Lance Edgar 53040dc6be fix: update references to get_class_hierarchy()
per upstream changes
2024-08-15 20:29:36 -05:00
Lance Edgar 1cacfab2a6 fix: tweak template for people/view_profile per wutta compat
wutta has the view defined but it returns minimal context
2024-08-15 18:44:14 -05:00
Lance Edgar bab09e3fe7 bump: version 0.15.6 → 0.16.0 2024-08-15 16:22:35 -05:00
Lance Edgar dd176a5e9e feat: add first wutta-based master, for PersonView
still opt-in-only at this point, the traditional tailbone-native
master is used by default.

new wutta master is not feature complete yet.  but at least things
seem to render and form posts work okay..

when enabled, this uses a "completely" wutta-based stack for the view,
grid and forms.  but the underlying DB is of course rattail, and the
templates are still traditional/tailbone.
2024-08-15 16:05:53 -05:00
Lance Edgar a6ce5eb21d feat: refactor forms/grids/views/templates per wuttaweb compat
this starts to get things more aligned between wuttaweb and tailbone.
the use case in mind so far is for a wuttaweb view to be included in a
tailbone app.

form and grid classes now have some new methods to match wuttaweb, so
templates call the shared method names where possible.

templates can no longer assume they have tailbone-native master view,
form, grid etc. so must inspect context more closely in some cases.
2024-08-15 15:49:54 -05:00
Lance Edgar b53479f8e4 bump: version 0.15.5 → 0.15.6 2024-08-13 11:21:38 -05:00
Lance Edgar 1f752530d2 fix: avoid before_render subscriber hook for web API
the purpose of that function is to setup extra template context, but
API views always render as 'json' with no template
2024-08-10 13:49:41 -05:00
Lance Edgar 2c46fde742 fix: simplify verbiage for batch execution panel 2024-08-10 08:43:54 -05:00
Lance Edgar d57efba381 bump: version 0.15.4 → 0.15.5 2024-08-09 19:48:51 -05:00
Lance Edgar f2fce2e305 fix: assign convenience attrs for all views (config, app, enum, model) 2024-08-09 19:22:26 -05:00
Lance Edgar b5f0ecb165 bump: version 0.15.3 → 0.15.4 2024-08-09 10:13:00 -05:00
Lance Edgar 7e683dfc4a fix: avoid bug when checking current theme
this check is happening not only for classic views but API as well,
which doesn't really have a theme..  probably need a proper fix in
wuttaweb but this should be okay for now
2024-08-09 10:11:38 -05:00
Lance Edgar 0b8315fc78 bump: version 0.15.2 → 0.15.3 2024-08-08 19:39:36 -05:00
Lance Edgar ffd694e7b7 fix: fix timepicker parseTime() when value is null 2024-08-08 19:39:01 -05:00
Lance Edgar 80dc4eb7a9 bump: version 0.15.1 → 0.15.2 2024-08-06 23:19:14 -05:00
Lance Edgar 518c108c88 fix: use auth handler, avoid legacy calls for role/perm checks 2024-08-06 10:36:20 -05:00
Lance Edgar bd1993f440 bump: version 0.15.0 → 0.15.1 2024-08-05 22:57:02 -05:00
Lance Edgar 91ea9021d7 fix: move magic b template context var to wuttaweb 2024-08-05 21:50:22 -05:00
Lance Edgar 2903b376b5 bump: version 0.14.5 → 0.15.0 2024-08-05 15:35:06 -05:00
Lance Edgar 9d2684046f feat: move more subscriber logic to wuttaweb 2024-08-05 15:00:11 -05:00
Lance Edgar 3b92bb3a9e fix: use wuttaweb logic for util.get_form_data() 2024-08-05 09:11:19 -05:00
Lance Edgar 5ec899cf08 bump: version 0.14.4 → 0.14.5 2024-08-03 17:43:46 -05:00
Lance Edgar 458c95696a fix: use auth handler instead of deprecated auth functions 2024-08-03 14:13:16 -05:00
Lance Edgar 08a89c490a fix: avoid duplicate partial param when grid reloads data 2024-07-21 20:20:43 -05:00
Lance Edgar a9495b6a70 bump: version 0.14.3 → 0.14.4 2024-07-18 17:59:55 -05:00
Lance Edgar 1bba6d9947 fix: fix more settings persistence bug(s) for datasync/configure
esp. for the profile consumers info
2024-07-18 17:58:59 -05:00
Lance Edgar f4f79f170a fix: fix modals for luigi tasks page, per oruga 2024-07-17 19:45:47 -05:00
Lance Edgar 9c466796da bump: version 0.14.2 → 0.14.3 2024-07-17 18:24:21 -05:00
Lance Edgar e88b8fc9bc fix: fix auto-collapse title for viewing trainwreck txn 2024-07-16 21:21:43 -05:00
Lance Edgar 3aafe578f0 fix: allow auto-collapse of header when viewing trainwreck txn 2024-07-16 18:59:35 -05:00
Lance Edgar af0f84762c bump: version 0.14.1 → 0.14.2 2024-07-15 21:52:05 -05:00
Lance Edgar be6eb5f815 fix: add null menu handler, for use with API apps 2024-07-15 21:51:45 -05:00
Lance Edgar 57fdacdb83 bump: version 0.14.0 → 0.14.1 2024-07-14 23:29:35 -05:00
Lance Edgar ece29d7b6c fix: update usage of auth handler, per rattail changes 2024-07-14 23:29:17 -05:00
Lance Edgar 5e1c0a5187 fix: fix model reference in menu handler 2024-07-14 12:41:08 -05:00
Lance Edgar 25e62fe6ef fix: fix bug when making "integration" menus
per recent refactor
2024-07-14 11:47:15 -05:00
Lance Edgar d70bac74f0 bump: version 0.13.2 → 0.14.0 2024-07-14 11:12:32 -05:00
Lance Edgar fd1ec01128 feat: move core menu logic to wuttaweb
tailbone still defines the default menus, and allows for making dynamic
menus from config (which wuttaweb does not).

also remove some even older logic for "v1" menu functions
2024-07-14 11:05:01 -05:00
Lance Edgar 0b4629ea29 bump: version 0.13.1 → 0.13.2 2024-07-13 15:28:59 -05:00
Lance Edgar 27214cc62f fix: fix logic bug for datasync/config settings save
dang it
2024-07-13 15:28:28 -05:00
Lance Edgar d2d0206b45 build: run pytest but avoid tox when preparing release
buildbot can let us know if something goes wrong with an atypical
python version etc.
2024-07-13 15:16:45 -05:00
Lance Edgar eede274529 bump: version 0.13.0 → 0.13.1 2024-07-13 15:15:51 -05:00
Lance Edgar ee781ec489 fix: fix settings persistence bug(s) for datasync/configure page
also hide the Changes context menu link, within the Configure page
2024-07-13 15:14:04 -05:00
Lance Edgar ca660f4087 bump: version 0.12.1 → 0.13.0 2024-07-12 09:38:12 -05:00
Lance Edgar ce156d6278 feat: begin integrating WuttaWeb as upstream dependency
the bare minimum, just to get the relationship established.  mostly
it's calling upstream subscriber / event hooks where applicable.

this also overhauls the docs config to use furo theme etc.
2024-07-12 09:35:34 -05:00
Lance Edgar e531f98079 fix: cast enum as list to satisfy deform widget
seems to only be an issue for deform 2.0.15+
2024-07-11 13:54:37 -05:00
Lance Edgar 09ce2d5a40 bump: version 0.12.0 → 0.12.1 2024-07-11 13:16:36 -05:00
Lance Edgar ae8212069c fix: refactor config.get_model() => app.model
per rattail changes
2024-07-11 13:16:27 -05:00
Lance Edgar 4eb5866379 bump: version 0.11.10 → 0.12.0 2024-07-09 16:45:45 -05:00
Lance Edgar a86a33445e feat: drop python 3.6 support, use pyproject.toml (again) 2024-07-09 16:45:36 -05:00
Lance Edgar 12f8b7bdf7 bump: version 0.11.9 → 0.11.10 2024-07-05 14:58:02 -05:00
Lance Edgar 2f2ebd0f07 fix: make the Members tab optional, for profile view
and hidden by default
2024-07-05 14:57:19 -05:00
Lance Edgar 2917463bb6 bump: version 0.11.8 → 0.11.9 2024-07-05 14:49:59 -05:00
Lance Edgar 16bf13787d fix: add optional Transactions tab for profile view
showing Trainwreck data by default
2024-07-05 14:45:35 -05:00
Lance Edgar b7d26b6b8c fix: add xref button to customer profile, for trainwreck txn view 2024-07-05 14:30:52 -05:00
Lance Edgar 19e65f5bb9 fix: expand input for butterball theme 2024-07-05 13:07:08 -05:00
Lance Edgar 735327e46b fix: improve collapse panels for butterball theme 2024-07-05 12:53:14 -05:00
Lance Edgar 2988ff3ee9 fix: do not show flash message when changing app theme
it is just distracting esp. when testing different themes
2024-07-05 12:50:45 -05:00
Lance Edgar 431a4d7433 bump: version 0.11.7 → 0.11.8 2024-07-04 23:59:06 -05:00
Lance Edgar 58be7e9d5b fix: add tool to make user account from profile view 2024-07-04 21:32:46 -05:00
Lance Edgar ddec77c37f fix: leverage import handler method to determine command/subcommand
just moved previous logic to rattail/handler
2024-07-04 20:35:14 -05:00
Lance Edgar 89d7009a18 fix: allow view supplements to add extra links for profile employee tab 2024-07-04 18:21:06 -05:00
Lance Edgar 793a15883e fix: fix grid action icons for datasync/configure, per oruga 2024-07-04 15:59:05 -05:00
Lance Edgar 76897c24de bump: version 0.11.6 → 0.11.7 2024-07-04 08:20:31 -05:00
Lance Edgar 5e11a2ecf6 fix: expand POD image URL setting input 2024-07-02 22:47:03 -05:00
Lance Edgar e23193b730 fix: cast enum as list to satisfy deform widget
seems to only be an issue for deform 2.0.15+
2024-07-02 16:45:10 -05:00
Lance Edgar 9146cdc835 fix: allow view supplements to add to profile member context 2024-07-02 14:20:48 -05:00
Lance Edgar 1f38894f02 fix: include edit profile email/phone dialogs only if user has perms
otherwise we get JS errors when page loads
2024-07-02 14:14:15 -05:00
Lance Edgar d72d6f8c7c fix: require zope.sqlalchemy >= 1.5
so we can do away with some old cruft, since latest zope.sqlalchemy is
3.1 from 2023-09-12
2024-07-02 11:14:03 -05:00
Lance Edgar aab4dec27e fix: add stacklevel to deprecation warnings 2024-07-02 09:05:51 -05:00
Lance Edgar db67630363 bump: version 0.11.5 → 0.11.6 2024-07-01 23:20:09 -05:00
Lance Edgar c887412825 fix: fix syntax bug 2024-07-01 19:06:04 -05:00
Lance Edgar 2feb07e1d3 fix: remove references, dependency for six package 2024-07-01 17:01:01 -05:00
Lance Edgar 6f8b825b0b fix: set explicit referrer when changing dbkey
since for some reason HTTP_REFERER is not always set now??
2024-07-01 15:23:56 -05:00
Lance Edgar cad50c9149 bump: version 0.11.4 → 0.11.5 2024-06-30 21:28:56 -05:00
Lance Edgar d6939e52b4 fix: use vue 3.4.31 and oruga 0.8.12 by default
i.e. for butterball theme

cf. https://github.com/oruga-ui/oruga/issues/974#issuecomment-2198573369
2024-06-30 18:25:01 -05:00
Lance Edgar 3f7de5872e fix: add custom url prefix if needed, for fanstatic 2024-06-30 12:40:03 -05:00
Lance Edgar 1dc632174e fix: allow comma in numeric filter input
just remove them and run with the remainder, on the SQL side
2024-06-30 11:44:33 -05:00
Lance Edgar eff5341335 bump: version 0.11.3 → 0.11.4 2024-06-30 10:49:54 -05:00
Lance Edgar 83e4d95741 fix: don't escape each address for email attempts grid
now that we are properly escaping the full cell value, no need
2024-06-30 10:32:05 -05:00
Lance Edgar 9b6447c4cb fix: require vendor when making new ordering batch via api
pretty sure this pattern needs to be expanded and probably improved,
but wanted to fix this one scenario for now, per error email
2024-06-28 17:58:27 -05:00
Lance Edgar ec5ed490d9 fix: start/stop being root should submit POST instead of GET
obviously it's access-restricted anyway but this just seems more correct

but more importantly this makes the referrer explicit, since for some
unknown reason i am suddenly seeing that be blank for certain installs
where that wasn't the case before (?) - and the result was that every
time you start/stop being root you would be redirected to home page
instead of remaining on current page
2024-06-28 17:35:54 -05:00
Lance Edgar d17bd35909 bump: version 0.11.2 → 0.11.3 2024-06-28 15:39:59 -05:00
Lance Edgar 3b7cc19faa fix: handle error when merging 2 records fails
should give the user some idea of the problem instead of just sending
error email to admins
2024-06-28 15:36:08 -05:00
Lance Edgar 067ca5bd43 fix: add link to "resolved by" user for pending products 2024-06-27 23:11:13 -05:00
Lance Edgar 525a28f3fe bump: version 0.11.1 → 0.11.2 2024-06-18 18:05:05 -05:00
Lance Edgar a0cd8835e0 fix: show flash error message if resolve pending product fails 2024-06-18 16:12:24 -05:00
Lance Edgar 231ca0363a fix: product records should be touchable 2024-06-18 16:06:55 -05:00
Lance Edgar 88e7d86087 fix: use different logic for buefy/oruga for product lookup keydown
i could have swore the new logic worked with buefy..but today it didn't
2024-06-18 15:04:05 -05:00
Lance Edgar 0212e52b66 fix: hide certain custorder settings if not applicable 2024-06-14 19:59:52 -05:00
Lance Edgar da4450b574 build: avoid version parse when uploading release 2024-06-14 18:02:39 -05:00
Lance Edgar ab4dbbedf0 bump: version 0.11.0 → 0.11.1 2024-06-14 18:01:40 -05:00
Lance Edgar 6e741f6156 fix: revert back to setup.py + setup.cfg
apparently with python 3.6 things "mostly" work but then they break if
any specified dependencies have a dot in the name.  which in this
project, is the case for `zope.sqlalchemy`

so until we drop python 3.6 support, we cannot use pyproject.toml here
2024-06-14 17:57:01 -05:00
Lance Edgar fb0c538a2b test: skip running tests for py36
we should soon require python 3.8 anyway
2024-06-10 17:42:29 -05:00
Lance Edgar f9cb6cb59b bump: version 0.10.16 → 0.11.0 2024-06-10 16:40:55 -05:00
Lance Edgar 1402d437b5 feat: switch from setup.cfg to pyproject.toml + hatchling 2024-06-10 16:23:38 -05:00
Lance Edgar dd58c640fa Update changelog 2024-06-10 11:11:06 -05:00
Lance Edgar 2c2727bf66 feat: standardize how app, package versions are determined 2024-06-10 09:14:20 -05:00
Lance Edgar b8ace1eb98 fix: avoid deprecated config methods for app/node title 2024-06-09 23:07:52 -05:00
Lance Edgar 7c3d5b46f3 Update changelog 2024-06-07 10:25:48 -05:00
Lance Edgar a849d8452b Update changelog 2024-06-07 10:25:14 -05:00
Lance Edgar 610e1666c0 Revert "Use pkg_resources to determine package versions"
This reverts commit f6f2a53a0c.
2024-06-07 10:07:31 -05:00
Lance Edgar 94d7836321 Ignore dist folder 2024-06-06 23:05:40 -05:00
Lance Edgar 0491d8517c Update changelog 2024-06-06 23:04:47 -05:00
Lance Edgar f6f2a53a0c Use pkg_resources to determine package versions
and always add `app_version` to global template context.  this was for
sake of "About This App v1.0.0" style links in custom page footers
2024-06-06 20:34:31 -05:00
Lance Edgar ce290f5f8b Update changelog 2024-06-06 15:30:48 -05:00
Lance Edgar d9911cf23d Add 'fanstatic' support for sake of libcache assets
for vue.js and oruga etc.

we don't want to include files in tailbone since they are apt to
change over time, and probably need to use different versions for
different apps etc.

much may need to change yet, this is a first attempt but so far it
seems quite promising
2024-06-05 23:04:45 -05:00
Lance Edgar 1afc70e788 Remove old/unused scaffold for use with pcreate
we now have a better Generate Project feature
2024-06-04 22:11:51 -05:00
Lance Edgar c189273471 Update changelog 2024-06-04 21:12:44 -05:00
Lance Edgar 22aceb4d67 Include butterball theme by default for new apps
but it is not "the" default yet..
2024-06-04 17:28:07 -05:00
Lance Edgar da6ccf4425 Fix product lookup component, per butterball 2024-06-04 17:16:57 -05:00
Lance Edgar d02bf0e5c7 Include extra styles from base_meta template for butterball 2024-06-04 17:15:42 -05:00
Lance Edgar 10aac388f0 Add <b-tooltip> component shim 2024-06-04 17:15:29 -05:00
Lance Edgar 00e2af1561 Set explicit referrer when changing app theme
to include url #hash value if there is one, so switching theme is more
seamless from the view profile page
2024-06-04 01:07:38 -05:00
Lance Edgar 6a7c06d26e Remove version cap for deform
see also commit 95dd8d83dc where i first
added the version cap; it mentions an error that i am not sure how to
reproduce.  so we'll see if there really is still an error..or if it
has since fixed itself
2024-06-03 23:16:00 -05:00
Lance Edgar efe477d0db Require pyramid 2.x; remove 1.x-style auth policies 2024-06-03 23:13:25 -05:00
Lance Edgar e17ef2edd8 Update changelog 2024-06-03 21:16:42 -05:00
Lance Edgar 30a8b8e5e4 Fix inventory worksheet generator, per butterball 2024-06-03 20:07:42 -05:00
Lance Edgar 2498da3909 Fix ordering worksheet generator, per butterball 2024-06-03 20:04:01 -05:00
Lance Edgar 2791e1c385 Fix grid bug for tempmon appliance view, per oruga 2024-06-03 19:51:16 -05:00
Lance Edgar 0303014acb Fix vue3 refresh for employee tab of profile view
and misc. related cleanup
2024-06-03 17:37:11 -05:00
Lance Edgar 9243edf7af Fix vue3 refresh for name, address cards in profile view 2024-06-03 16:51:29 -05:00
Lance Edgar ab523719a6 Update changelog 2024-06-03 11:16:33 -05:00
Lance Edgar b27987f1d1 More butterball fixes for "view profile" template 2024-06-03 11:15:22 -05:00
Lance Edgar 30238528fe Fix focus for <b-select> shim component 2024-06-03 10:48:59 -05:00
Lance Edgar 29c9ea1a2b Update changelog 2024-06-03 10:28:42 -05:00
Lance Edgar 58f9588261 Fix the "new custorder" page for butterball
reasonably confident this one is complete, and didn't break buefy theme..
2024-06-02 19:56:42 -05:00
Lance Edgar 3dc8deef67 Fix panel style for PO vs. Invoice breakdown in receiving batch 2024-06-02 15:58:10 -05:00
Lance Edgar fa25857680 Let master view control context menu items for page
that really does not belong in the template if we can help it.  some
templates still define context menu items but can hopefully phase
those out over time
2024-06-02 15:54:42 -05:00
Lance Edgar 254df6d6f2 Update changelog 2024-06-02 14:55:18 -05:00
Lance Edgar 40edde2694 Use oruga 0.8.9 by default 2024-06-01 23:06:19 -05:00
Lance Edgar 9258237b85 Allow per-user custom styles for butterball 2024-06-01 21:37:38 -05:00
Lance Edgar 1bf28eb286 Fix product view template for oruga/butterball 2024-06-01 19:07:07 -05:00
Lance Edgar 77eeb63b62 Add styling for checked grid rows, per oruga/butterball 2024-06-01 17:46:35 -05:00
Lance Edgar 43db60bbee Update changelog 2024-06-01 14:26:17 -05:00
Lance Edgar 6b1c313efd Fix file upload widget for oruga 2024-06-01 14:23:35 -05:00
Lance Edgar d05458c7fb Add speedbumps for delete, set preferred email/phone in profile view 2024-06-01 13:40:50 -05:00
Lance Edgar b87b1a3801 Escape all unsafe html for grid data 2024-05-31 21:20:45 -05:00
Lance Edgar ba519334d1 Fix overflow when instance header title is too long 2024-05-31 18:01:56 -05:00
Lance Edgar 3ac131cb51 Add column filters for import/export main grid 2024-05-31 14:51:01 -05:00
Lance Edgar 9b88f01378 Log error if registry has no rattail config
not clear if this is even possible, but if so i want to know about it

trying to figure out the occasional error email we get, latest being
from collectd/curl pinging the /login page, but request.has_perm()
call fails with missing attr?!

seems like either the rattail config is empty, or else the subscriber
events aren't firing (in the correct order) ?
2024-05-31 12:06:31 -05:00
Lance Edgar 49cd050272 Add setting to allow decimal quantities for receiving 2024-05-31 10:57:28 -05:00
Lance Edgar 0d8928bdf5 Update changelog 2024-05-29 22:15:39 -05:00
Lance Edgar 54b75dbe1a Fix basic problems with people profile view, per butterball
plenty more tweaks needed yet i'm sure, but page looks reasonable now
at least
2024-05-29 20:47:10 -05:00
Lance Edgar b98d651144 Expose quickie lookup for butterball theme 2024-05-29 16:55:42 -05:00
Lance Edgar 9a841ba5e2 Expose db picker for butterball theme 2024-05-29 16:33:30 -05:00
Lance Edgar 4ccdf99a43 Add way to flag organic products within lookup dialog 2024-05-29 15:47:04 -05:00
Lance Edgar f8ab8d462c Update changelog 2024-05-29 09:40:50 -05:00
Lance Edgar fb9bc01939 Add <tailbone-timepicker> component for oruga 2024-05-14 12:17:50 -05:00
Lance Edgar ec61444b3d Update changelog 2024-05-12 17:41:09 -05:00
Lance Edgar 66304a418e Fix styles for grid actions, per butterball 2024-05-10 15:50:33 -05:00
Lance Edgar 6bb6c16bc7 Update changelogo 2024-05-10 15:00:21 -05:00
Lance Edgar c43deb1307 Fix bug with grid date filters 2024-05-08 20:50:54 -05:00
Lance Edgar b65b514270 Update changelog 2024-05-08 11:17:58 -05:00
Lance Edgar 9b65e18261 Tweak styles for grid action links, per butterball 2024-05-08 11:16:16 -05:00
Lance Edgar b40423fc2d Fix "view receiving row" page, per oruga
all the buttons and tools *should* work correctly for Vue 2 and 3 now
2024-05-07 20:44:26 -05:00
Lance Edgar 28fb3f44a7 More data type fixes for <tailbone-datepicker>
traditionally the caller has always dealt with string values only, so
the component should never emit events with date values, etc.
2024-05-07 18:20:26 -05:00
Lance Edgar d607ab2981 Fix display for "view receiving row" page, per oruga
this page still needs help; "Account for Product" is broken for oruga
2024-05-07 12:43:07 -05:00
Lance Edgar 9cd648f78f Fix button text for autocomplete
whoops i think that was a debug thing i forgot to remove
2024-05-07 11:57:00 -05:00
Lance Edgar 703d583f6f Fix "tools" helper for receiving batch view, per oruga 2024-05-07 11:56:57 -05:00
Lance Edgar f0d694cfe5 Rename some attrs etc. for buefy components used with oruga 2024-05-06 22:56:47 -05:00
Lance Edgar 3d319cbd09 Fix login "enter" key behavior, per oruga 2024-05-06 22:13:43 -05:00
Lance Edgar e4c4259674 Remove version restriction for pyramid_beaker dependency
latest version is 0.9, so this wasn't all that relevant
2024-05-06 21:53:11 -05:00
Lance Edgar 15fedf5976 Fix employees grid when viewing department (per oruga) 2024-05-06 21:52:53 -05:00
Lance Edgar 68384a00dc Update changelog 2024-04-28 20:25:55 -05:00
Lance Edgar e9ddd6dc36 Stop including 'falafel' as available theme 2024-04-28 20:21:20 -05:00
Lance Edgar 6ce65badeb Show "View This" button when cloning a record 2024-04-28 20:12:49 -05:00
Lance Edgar 9ee6521d6a Fix upgrade execution logic/UI per oruga 2024-04-28 20:12:06 -05:00
Lance Edgar 72f48fa963 Fix vertical alignment in main menu bar, for butterball 2024-04-28 19:30:35 -05:00
Lance Edgar b3784dcc4a Update various icon names for oruga compatibility 2024-04-28 18:49:11 -05:00
Lance Edgar 34878f9293 Sort list of available themes
and add `computed` attr for WholePage; needed by some customizations
2024-04-28 18:37:00 -05:00
Lance Edgar adaa39f572 Update changelog 2024-04-28 17:33:06 -05:00
Lance Edgar 1d5a0630ef Change default URL for some vue3+oruga libs
apparently the first ones were not ideal / optimized, but these are
2024-04-28 16:16:38 -05:00
Lance Edgar 855fa7e1e2 Fix centering for "Show Totals" grid tool 2024-04-28 02:41:45 -05:00
Lance Edgar f2f023e7b3 Fix v-model handling for grid-filter-numeric-value 2024-04-28 02:39:40 -05:00
Lance Edgar 33251e880e Fix oruga styles for batch view
also use typical panels, for row status breakdown etc.
2024-04-28 01:58:19 -05:00
Lance Edgar 358816d9e7 Add oruga overhead for "classic" app only, not API 2024-04-28 00:51:07 -05:00
Lance Edgar 362d545f34 Fix modal state for appinfo/configure page 2024-04-28 00:25:03 -05:00
Lance Edgar fb81a8302c Use oruga 0.8.7 by default instead of latest 0.8.8
until the new bug is fixed, https://github.com/oruga-ui/oruga/issues/913
2024-04-28 00:20:43 -05:00
Lance Edgar e7a44d9979 Let caller use string data for <tailbone-datepicker>
don't require a Date object, since callers thus far have not expected that
2024-04-27 21:54:55 -05:00
Lance Edgar 2eaeb1891d Add initial support for Vue 3 + Oruga, via "butterball" theme
just a savepoint, still have lots to do and test before this really works
2024-04-27 21:06:20 -05:00
Lance Edgar 5aa8d1f9a3 Use buefy table for "find principal by perm" results
this should work for oruga as well
2024-04-27 19:17:30 -05:00
Lance Edgar 098ed5b1cf Improve keydown handling for grid Add Filter autocomplete
should work the same, but this way also works with oruga
2024-04-27 19:17:30 -05:00
Lance Edgar 890ec64f3c Misc. template cleanup per oruga effort 2024-04-27 19:17:27 -05:00
Lance Edgar ba32422059 Fix bug when saving user preferences theme
it was being saved even when it should have been empty value
2024-04-25 23:56:21 -05:00
Lance Edgar 8b3a9c9dad Use simple field labels when possible
only use template if it must include icons etc.
2024-04-25 22:49:37 -05:00
Lance Edgar 2a22e8939c Add index title to Change Password page 2024-04-25 22:17:59 -05:00
Lance Edgar 6bee65780c Improve logic for Add Filter grid button/autocomplete
this should work for oruga as well as buefy
2024-04-25 22:00:01 -05:00
Lance Edgar e030dc841d Expand some modal fields, per oruga styles 2024-04-25 21:31:26 -05:00
Lance Edgar 25a27af29c Use explicit flex styles instead of "level" for grid filters etc.
just to be more precise, and consistent
2024-04-25 20:45:03 -05:00
Lance Edgar daf68cad01 Fix data type handling for datepicker and grid filter components
here is what's up now:

- <b-datepicker> expects v-model to be a Date
- <tailbone-datepicker> also expects a Date
- <grid-filter-date-value> uses String for its v-model

latter is so the value can represent a date range, e.g. 'YYYY-MM-DD|YYYY-MM-DD'

anyway there was previously confusion about data type among these
components, and hopefully they are straight now per the above outline
2024-04-25 18:52:34 -05:00
Lance Edgar ab57fb3f0f Tweak flex styles for grid filters 2024-04-25 18:16:39 -05:00
Lance Edgar f43259fbc1 Use proper flex styles for grid pagination footer 2024-04-25 16:04:30 -05:00
Lance Edgar bfe6b5bc25 Use explicit flex styles for grid-tools element
and so, must ensure children of grid-tools are atomic elements
2024-04-25 15:41:06 -05:00
Lance Edgar 23e6eef604 Update changelog 2024-04-25 14:05:10 -05:00
Lance Edgar d2aa91502a Allow deleting rows from executed batches
requires a view to explicitly opt-in.  and a separate permission is
required for the user
2024-04-25 14:02:45 -05:00
Lance Edgar 4f6ee1fb22 Use v-model to track selection etc. for download results fields 2024-04-24 22:10:56 -05:00
Lance Edgar ddafa9ed97 Tweak icon for Download Results button
make it more portable for oruga
2024-04-24 20:19:15 -05:00
Lance Edgar 0ca3b31b2e Use normal button for grid filters
since that's more portable (for oruga) than "checkbox button"
2024-04-24 18:20:16 -05:00
Lance Edgar 9f984241c4 Cleanup grid/filters logic a bit
get rid of grids.js file, remove filter templates from complete.mako

move all that instead to filter-components.mako

for now, base template does import + setup for the latter, "just in
case" a given view has any grids.  each grid should (still) be
isolated but no code should be duplicated now.  whereas before the
grid filter templates were in comlete.mako and hence could be declared
more than once if multiple grids are on a page
2024-04-24 17:43:22 -05:00
Lance Edgar d6fa83cd87 Fix permission checks for root user with pyramid 2.x 2024-04-19 22:27:30 -05:00
Lance Edgar 8781e34c98 Rename setting for custom user css (remove "buefy")
but have to keep support for older setting name for now
2024-04-19 21:18:57 -05:00
Lance Edgar 49da9776e7 Remove unused test fixtures 2024-04-19 20:25:07 -05:00
Lance Edgar 36b9e00dc9 Remove unused code for webhelpers2_grid 2024-04-19 20:15:44 -05:00
Lance Edgar 5cb643a32a Update changelog 2024-04-19 19:47:41 -05:00
Lance Edgar 1fa6e35663 Remove config "style" from appinfo page
there is only one style now (finally)
2024-04-19 17:45:58 -05:00
Lance Edgar e82f0f37d8 Fix raw query to avoid SQLAlchemy 2.x warnings 2024-04-16 23:29:56 -05:00
Lance Edgar 7fa39d42e2 Fix ASGI websockets when serving on sub-path under site root 2024-04-16 23:27:50 -05:00
Lance Edgar a95cc2b9e8 Update changelog 2024-04-16 21:14:23 -05:00
Lance Edgar e7b8b6e818 Fix master template bug when no form in context 2024-04-16 21:13:53 -05:00
Lance Edgar 5a7deadba2 Update changelog 2024-04-16 20:11:15 -05:00
Lance Edgar 9065f42195 Fix typo when getting app instance 2024-04-16 20:10:10 -05:00
Lance Edgar b37981e83f Prevent multi-click for grid filters "Save Defaults" button 2024-04-16 20:09:39 -05:00
Lance Edgar 0d9c5a078b Improve form support for view supplements
this seems a bit hacky yet but works for now..

cf. field logic for Vendor -> Quickbooks Bank Accounts, which requires this
2024-04-16 18:21:59 -05:00
Lance Edgar c35c0f8b61 Update changelog 2024-04-16 10:44:33 -05:00
Lance Edgar 8b4b3de336 Add support for Pyramid 2.x; new security policy
custom apps are still free to use pyramid 1.x

new security policy is only used if config file says so
2024-04-16 09:48:29 -05:00
Lance Edgar 85d62a8e38 Reminder to improve css hack for datepicker in modal 2024-04-15 13:31:42 -05:00
Lance Edgar 52c8f3e12c Rename custom user_css context
and stop checking an older deprecated setting
2024-04-15 13:31:35 -05:00
Lance Edgar d0d568b3a5 Escape underscore char for "contains" query filter
since underscore has special meaning for LIKE clause
2024-04-15 12:44:46 -05:00
Lance Edgar 666c16b74e Fix default dist filename for release task
not sure why this fix was needed, did setuptools behavior change?
2024-04-15 10:58:16 -05:00
Lance Edgar 2f115c0717 Update changelog 2024-04-15 10:56:49 -05:00
Lance Edgar d4089fbc6e Some more tweaks to remove "buefy" references
mostly just docstring / comments but there were some code changes too
2024-04-14 20:56:11 -05:00
Lance Edgar ba521abf4f Remove some references to "buefy" name within docstrings, comments 2024-04-14 20:30:52 -05:00
Lance Edgar c036932ce4 Remove several references to "buefy" name
class methods, template filenames, etc.

also made various edits per newer conventions
2024-04-14 19:54:29 -05:00
Lance Edgar 96ba039299 Rename grids/complete template (avoid buefy name)
and rename grid methods accordingly
2024-04-13 10:13:51 -05:00
Lance Edgar 1103b09a76 Rename forms/deform template (drop buefy suffix)
for now, deprecate `form.render()` method and just use
`render_deform()` - but probably should change that to something
else eventually..?
2024-04-13 09:45:10 -05:00
Lance Edgar cd7c1bba21 Rename template for grid filters (drop buefy suffix)
also remove some deprecated functions
2024-04-13 09:21:48 -05:00
Lance Edgar 1973614840 Rename people "view_profile" template (drop buefy suffix) 2024-04-13 09:09:23 -05:00
Lance Edgar cbbd77c49c Show toast msg instead of silent error, when grid fetch fails
specifically, if a user clicks "Save defaults" for the grid filters,
but they aren't currently logged in, error will ensue.

this is a bit of an edge case which IIUC would require multiple tabs
etc. but still is worth avoiding an error email from it.
2024-04-11 16:58:12 -05:00
Lance Edgar aa500351ed Avoid error for tax field when creating new department
someday should fix that for real..
2024-04-11 16:37:55 -05:00
Lance Edgar de8751b86c Try to return JSON error when receiving API call fails
although in my testing, the error still got raised somehow in the
tweens or something?  client then sees it as a 500 response and gets
no JSON
2024-04-11 14:14:27 -05:00
Lance Edgar a1b05540be Avoid uncaught error when updating order batch row quantities 2024-04-10 12:24:13 -05:00
Lance Edgar e0dc858451 Update changelog 2024-04-01 18:28:39 -05:00
Lance Edgar 1889f7d269 Add basic CRUD for Person "preferred first name"
only shown if config flag says so
2024-04-01 18:26:18 -05:00
Lance Edgar cdc857065b Update changelog 2024-03-27 13:14:23 -05:00
Lance Edgar dfdb7a9b59 Fix bulk-delete rows for import/export batch
per changes in SQLAlchemy 1.4
2024-03-27 13:13:33 -05:00
Lance Edgar 4363b7c5d7 Update changelog 2024-03-26 12:53:20 -05:00
Lance Edgar 27fce173ce Fix how row grid values are fetched, for row proxy objects
per changes coming in SQLAlchemy 2.0
2024-03-26 11:48:52 -05:00
Lance Edgar 0b7d2f5aed Fix how metadata/bind is used for importer batch table
per changes coming in SQLAlchemy 2.0
2024-03-26 11:47:37 -05:00
Lance Edgar 25c48a97c5 Update changelog 2023-12-26 20:17:05 -06:00
Lance Edgar a40add8f41 Expose default custorder discount for Departments 2023-12-22 11:50:05 -06:00
Lance Edgar 3bdc7175a3 Use common logic to render invoice total for receiving
and avoid error if total is none
2023-12-20 11:56:24 -06:00
Lance Edgar 90e35ee3db Hide single invoice file field for multi-invoice receiving batch 2023-12-19 12:49:33 -06:00
Lance Edgar 90630fe852 Auto-disable submit button for login form
not sure why i had explicitly disabled that before..?
2023-12-13 12:05:54 -06:00
Lance Edgar b6618c8ee5 Update changelog 2023-12-12 11:46:28 -06:00
Lance Edgar 98fc82acfd Use ltrim(rtrim()) instead of just trim() in grid filters
apparently this is needed for older SQL Server compatibility, per
https://stackoverflow.com/questions/54340470/trim-is-not-a-recognized-built-in-function-name
2023-12-11 13:50:02 -06:00
Lance Edgar 91e7001963 Overhaul tox config for more python versions 2023-12-04 10:15:12 -06:00
Lance Edgar d154986128 Update changelog 2023-12-01 21:57:20 -06:00
Lance Edgar 3e4bbf7092 Use clientele handler to populate customer dropdown widget 2023-12-01 19:50:07 -06:00
Lance Edgar faeb2cb7e2 Update changelog 2023-11-30 18:25:01 -06:00
Lance Edgar 35131c8732 Provide a way to show enum display text for some version diff fields
master view must explicitly declare which enums for which fields
2023-11-30 18:23:47 -06:00
Lance Edgar 2a9d5f74ce Update changelog 2023-11-30 15:17:01 -06:00
Lance Edgar f4cb1cb097 Avoid error when editing a department
just a temp hack, need to fix proper yet
2023-11-29 15:03:08 -06:00
Lance Edgar e23998a88b Update changelog 2023-11-19 22:24:15 -06:00
Lance Edgar e39581695f Fix DB picker, theme picker per Buefy conventions 2023-11-17 17:00:50 -06:00
Lance Edgar dd9e41f651 Update changelog 2023-11-15 11:42:07 -06:00
Lance Edgar 97e7026cc9 Avoid outright error if user scans barcode for inventory count 2023-11-15 09:46:23 -06:00
Lance Edgar 853cc871f7 Remove reference to pytz library 2023-11-11 21:26:11 -06:00
Lance Edgar fc96fb40fb Log warning instead of error for batch population error
this is most typically caused by bad user input; a warning is shown on
screen so they hopefully can guess what the problem is.  no need to
loop in the admins via email
2023-11-05 18:31:43 -06:00
Lance Edgar 172fe6c49c Update changelog 2023-11-05 17:10:32 -06:00
Lance Edgar 9fa592c5d6 Expose status code for equity payments 2023-11-05 16:57:14 -06:00
Lance Edgar bbffe1dc82 Update changelog 2023-11-01 20:54:39 -05:00
Lance Edgar 55a115e57a Add button to confirm all costs for receiving 2023-11-01 20:53:11 -05:00
Lance Edgar 7ab3d2b635 Update changelog 2023-11-01 19:45:35 -05:00
Lance Edgar 51d7c10bc5 Fix config key for default themes list 2023-11-01 19:44:44 -05:00
Lance Edgar b13fc99e95 Use shared logic to get batch handler 2023-11-01 19:43:46 -05:00
Lance Edgar b231c194a4 Update changelog 2023-11-01 17:48:28 -05:00
Lance Edgar b5da5a46de Avoid error when rendering version diff
can't always assume relationship entities are versioned
2023-11-01 17:47:07 -05:00
Lance Edgar 8522123cd3 Encode values for "between" query filter 2023-11-01 14:54:30 -05:00
Lance Edgar bae6bc2133 Update changelog 2023-11-01 09:20:26 -05:00
Lance Edgar 2f70ce2d5c Fix missing import 2023-11-01 09:20:03 -05:00
Lance Edgar 7ac505f1f4 Update changelog 2023-11-01 08:14:09 -05:00
Lance Edgar f47e45a928 Add deprecation warnings for ambgiguous config keys 2023-11-01 08:13:36 -05:00
Lance Edgar a9ab59eb92 Update changelog 2023-10-30 01:06:41 -05:00
Lance Edgar a0075f6f78 Log warning / avoid error if email profile can't be normalized
e.g. if some import error happens
2023-10-29 22:22:16 -05:00
Lance Edgar 8b07289452 Update changelog 2023-10-29 15:59:17 -05:00
Lance Edgar c1f2f84c7f Remove unused "simple menus" module approach
now we always use a handler instead
2023-10-29 15:46:18 -05:00
Lance Edgar da13254caa Tweak param docs for Form.set_validator() 2023-10-29 15:10:56 -05:00
Lance Edgar fe4a178d43 Add way to "ignore" a pending product
and some related tweaks for sake of grid
2023-10-26 20:43:12 -05:00
Lance Edgar 1fc17658ff Update changelog 2023-10-26 18:44:38 -05:00
Lance Edgar a5c1cba81b Use product lookup component for "resolve pending product" tool 2023-10-26 10:06:00 -05:00
Lance Edgar 4809cf039e Update changelog 2023-10-25 20:22:48 -05:00
Lance Edgar a812181466 Expand the "product lookup" component to include autocomplete 2023-10-25 20:10:21 -05:00
Lance Edgar 441a6e5e0c Add separate perm for making new custorder for unknown product 2023-10-25 14:06:40 -05:00
Lance Edgar b5c68831b5 Do not show profile buttons for inactive customer shoppers 2023-10-25 12:20:04 -05:00
Lance Edgar cf1ef23996 Add column_only kwarg for Grid.set_label() method
pass True to affect only the column label and not the filter
2023-10-25 11:40:52 -05:00
Lance Edgar 70cc754f3e Use <b-select> for theme picker
instead of webhelpers2.html.tags.select() which seems to break for me
in dev now with python 3.10
2023-10-25 10:45:33 -05:00
Lance Edgar 72dda3771e Add price confirm prompt when adding unknown item to custorder
optional, per config
2023-10-24 19:51:27 -05:00
Lance Edgar 4247804707 Allow pending product fields to be required, for new custorder 2023-10-24 19:17:36 -05:00
Lance Edgar e308108bf7 Show user warning if "add item to custorder" fails
specifically, if user enters alpha chars for cost/price fields
2023-10-24 17:48:08 -05:00
Lance Edgar f708cb0b25 Fix bug when editing vendor 2023-10-24 17:44:02 -05:00
Lance Edgar 1f3877b7cb Update changelog 2023-10-24 09:54:31 -05:00
Lance Edgar 72e4daafc1 Fix config file priority for display, and batch subprocess commands 2023-10-24 09:53:40 -05:00
Lance Edgar 549976dcfb Update changelog 2023-10-24 09:27:12 -05:00
Lance Edgar 756b4b9d18 No need to configure logging
since the rattail config object will do that when first made
2023-10-23 20:35:43 -05:00
Lance Edgar f70772fabc Allow override of version diff for master views
and misc. other tweaks
2023-10-23 15:48:48 -05:00
Lance Edgar ec8a8d5ddc Update changelog 2023-10-23 13:06:38 -05:00
Lance Edgar 6d79766b24 Stop using sa-filters for basic grid sorting
this just breaks if we need to use "aliased" models e.g. when sorting
and/or filtering by Product "regular price" column and similar.  so
now sorting more like we always used to, except for multi-column.

nb. this still assumes callers use `Grid.make_sorter()` when declaring
the sorters.  if caller must specify more custom/explicit sort logic
then it likely will not work and we'll have to add a workaround to
allow avoiding the common logic..but that's another day
2023-10-21 16:10:36 -05:00
Lance Edgar 421266e70c Show more customer attrs for POS batch 2023-10-20 14:29:45 -05:00
Lance Edgar d87de1dc4f Expose permission for POS batch, toggle training mode 2023-10-19 20:48:52 -05:00
Lance Edgar dc99828b66 Show food stamp tender info for POS batch 2023-10-19 19:12:28 -05:00
Lance Edgar 5e8ea67773 Include invoice number for receiving batch row API 2023-10-19 14:57:06 -05:00
Lance Edgar 0d30247353 Add validtion to prevent duplicate files for multi-invoice receiving
by comparing sha256 hash values for each file
2023-10-19 14:03:25 -05:00
Lance Edgar aaf6f05820 Remove sorter for "Credits?" column in purchasing batch row grid
too convoluted, and broken per recent sort overhaul
2023-10-19 13:02:17 -05:00
Lance Edgar 954a2b78be Expose new price fields for POS batch row 2023-10-18 21:25:32 -05:00
Lance Edgar 230a54cb99 Fix default grid filter when "local" date times are involved 2023-10-18 21:25:13 -05:00
Lance Edgar 13565d1c45 Avoid "None" when rendering product UOM field 2023-10-18 21:24:37 -05:00
Lance Edgar 919d8d109f Use Grid.make_sorter() instead of legacy code
b/c multi-column sorting relies on this
2023-10-18 18:18:55 -05:00
Lance Edgar 659f5a8fe1 Replace dropdowns with autocomplete, for "find principals by perm" 2023-10-18 17:35:14 -05:00
Lance Edgar f86cc83996 Fix order xlsx download if missing order date 2023-10-17 15:26:22 -05:00
Lance Edgar 7525aaaa87 Expose more permissions for POS 2023-10-12 16:31:44 -05:00
Lance Edgar 115e95b9a8 Update changelog 2023-10-12 10:37:12 -05:00
Lance Edgar 5940778189 Fix version child classes for Customers view
must be sure to include any supplements
2023-10-12 10:33:56 -05:00
Lance Edgar 1a15d70568 Add some awareness of suspend/resume for POS batch 2023-10-11 23:11:23 -05:00
Lance Edgar d66dd5f199 Add permission for testing error handling at POS 2023-10-11 19:55:51 -05:00
Lance Edgar 507a9ffc71 Expose department tax, FS flag 2023-10-11 18:35:35 -05:00
Lance Edgar cd82f8927b Fix grid sorting when column key/name differ 2023-10-11 16:13:20 -05:00
Lance Edgar cddec51582 Update changelog 2023-10-11 15:56:16 -05:00
Lance Edgar 78deb5d09a Use autocomplete instead of dropdown for grid "add filter" 2023-10-10 22:10:01 -05:00
Lance Edgar 4328b9e385 Show full version history within the "view" page
avoid full page loads when navigating version history
2023-10-10 11:02:02 -05:00
Lance Edgar 44112a3a4b Allow null for FalafelDateTime form fields 2023-10-09 15:50:41 -05:00
Lance Edgar 9efe767654 Add smarts to show display text for some version diff fields
e.g. show `str(customer)` along with `customer_uuid` since almost
nobody will "care" about the uuid so much, they just want the name
2023-10-09 00:19:29 -05:00
Lance Edgar edb5393cdc Add front-end support for multi-column grid sorting
user must ctrl-click column header to engage multi-sort
2023-10-08 16:38:24 -05:00
Lance Edgar 6d7754cf2a Add back-end support for multi-column grid sorting
or very nearly, anyway.  front-end still just supports 1 column yet
2023-10-08 14:29:01 -05:00
Lance Edgar 4beca7af20 Make grid JS loadAsyncData() method truly async
not sure what this does but it seems to work, we'll see
2023-10-07 20:13:41 -05:00
Lance Edgar a201072a9d Update changelog 2023-10-07 18:57:03 -05:00
Lance Edgar 07b1d0841e Improve views for taxes, esp. in POS batches 2023-10-07 16:26:33 -05:00
Lance Edgar eccb855d09 Expose tender ref in POS batch rows; new tender flags 2023-10-06 20:34:14 -05:00
Lance Edgar 2f4877a264 Add "mark complete" button for inventory batch row entry page 2023-10-06 15:53:17 -05:00
Lance Edgar d84b98041f Avoid deprecated logic for fetching vendor contact email/phone 2023-10-06 15:03:17 -05:00
Lance Edgar 2ae2cdc4bd Update changelog 2023-10-06 10:13:18 -05:00
Lance Edgar d1d781966f Fix bug for param helptext in New Report page 2023-10-06 10:12:38 -05:00
Lance Edgar 53cf771c81 Update changelog 2023-10-06 10:00:37 -05:00
Lance Edgar d45ee34b0c Expose permissions for POS, if so configured 2023-10-06 09:16:25 -05:00
Lance Edgar e1a64de205 Fix bug in POS batch view 2023-10-05 19:59:57 -05:00
Lance Edgar b30f6cdf3a Fix CRUD pages for tempmon clients, probes
for some reason if helptext had embedded newlines, it would now fail
to render the form altogether.  guess that is a result of recent
change to e.g. `<b-field :message="['foo', 'bar']">` logic,
somehow.. anyway hopefully this fixes and no more surprises
2023-10-05 13:11:05 -05:00
Lance Edgar 3dfab8e42d Update changelog 2023-10-04 13:56:22 -05:00
Lance Edgar 7bae01f03c Improve master view oneoff_import() method
be more flexible about what caller must provide
2023-10-04 13:07:26 -05:00
Lance Edgar f3dddf0e40 Avoid deprecated pretty_hours() function 2023-10-04 11:56:50 -05:00
Lance Edgar 0b7791070f Update changelog 2023-10-04 10:59:54 -05:00
Lance Edgar 4125be7e8d Re-work FalafelDateTime logic a bit
need to be more "standard" in how (de)serialize works etc.

also be sure to show error messages if present, not just field helptext
2023-10-02 09:54:34 -05:00
Lance Edgar 746e13d134 Expose cash-back flags for tenders 2023-10-01 18:54:56 -05:00
Lance Edgar b7ccc6ea07 Use enum to display POS_ROW_TYPE 2023-10-01 17:31:33 -05:00
Lance Edgar a6bc3fb793 Update changelog 2023-10-01 12:09:32 -05:00
Lance Edgar 9f7e70f240 Add support for void rows in POS batch 2023-09-30 21:08:01 -05:00
Lance Edgar 0ee6725188 Tidy up logic for vendor filtering in products grid
was hoping to "fix" count issue but alas..

refs #23
2023-09-28 10:56:15 -05:00
Lance Edgar f572757f00 Expose views for tenders, more columns for POS batch/rows 2023-09-27 17:13:49 -05:00
Lance Edgar abcf1e1895 Add clone support for POS batches
just for testing of course..
2023-09-26 17:52:17 -05:00
Lance Edgar a9e9474f5c Do not allow executing custorder if no customer is set
or really any reason, as defined by handler
2023-09-26 09:32:57 -05:00
Lance Edgar a11be5a1e1 Update changelog 2023-09-25 19:41:59 -05:00
Lance Edgar e23b2f8711 Add custom form type/widget for time fields
ugh this still isn't that great, but making progress overall
2023-09-25 19:22:02 -05:00
Lance Edgar 032d37194f Update changelog 2023-09-25 18:06:16 -05:00
Lance Edgar 3e56950872 Expose POS batch views as "typical" 2023-09-24 19:30:59 -05:00
Lance Edgar 5a2612acab Update changelog 2023-09-24 14:47:54 -05:00
Lance Edgar bda05aed86 Use header button instead of link for "touch" instance 2023-09-24 08:37:50 -05:00
Lance Edgar 91ac1a9031 Show customer for POS batches 2023-09-23 20:08:40 -05:00
Lance Edgar 53e8c15267 Add basic views for POS batches 2023-09-23 11:14:43 -05:00
Lance Edgar d329b2945c Show "true" (calculated) equity total in members grid
pretty sure will need to tweak this but wanted something in place at least
2023-09-21 14:39:18 -05:00
Lance Edgar abca0115a6 Add remove_sorter() method for grids 2023-09-21 14:37:33 -05:00
Lance Edgar 3d6cc8a490 Show yesterday by default for Trainwreck if so configured 2023-09-20 18:13:52 -05:00
Lance Edgar 836fc0bf5b Update changelog 2023-09-19 16:37:05 -05:00
Lance Edgar 510b8383a4 Show catalog/invoice costs as 2-decimal currency in receiving 2023-09-19 15:03:16 -05:00
Lance Edgar 8b15f1304f Use small text input for receiving cost editor fields 2023-09-19 14:45:48 -05:00
Lance Edgar 6274e33a8c Prevent catalog/invoice cost edits if receiving batch is complete 2023-09-19 14:41:15 -05:00
Lance Edgar 1f97d4f5e5 Add link to vendor name for receiving batches grid 2023-09-19 14:40:58 -05:00
Lance Edgar b566549d15 Update changelog 2023-09-18 18:40:51 -05:00
Lance Edgar 4d8c8b199c Fix bug for new receiving from scratch via API 2023-09-18 18:37:41 -05:00
Lance Edgar d1d69e9488 Show user warning if receive quick lookup fails
just b/c a UPC doesn't exist yet doesn't prevent the batch from (in
some cases) adding a row for "unknown product" - but if the UPC is
sufficiently invalid, that can't happen
2023-09-18 18:28:11 -05:00
Lance Edgar a01fd62899 Update changelog 2023-09-17 21:21:10 -05:00
Lance Edgar 70956a2c47 Tweaks to improve handling of "missing" items for receiving 2023-09-17 18:30:38 -05:00
Lance Edgar e894d1d1f4 Include PO number for receiving batch details via API 2023-09-17 18:03:30 -05:00
Lance Edgar cc7b9ccb86 Avoid error when history has blanks for ordering worksheet 2023-09-17 17:23:59 -05:00
Lance Edgar a807a0f50c Add "falafel" custom date/time field type and widget
finally able to edit datetime fields, but feels like a lot of
assumptions to make, just to determine time zone..so keeping naive UTC
on the backend still, and naive local on the frontend

in general this needs more polish, but is a start..
2023-09-16 20:01:32 -05:00
Lance Edgar 99065548ff Update changelog 2023-09-16 13:06:54 -05:00
Lance Edgar df897aef13 Make member key field readonly when viewing equity payment 2023-09-16 13:06:26 -05:00
Lance Edgar 1cfc275eae Update changelog 2023-09-15 19:30:27 -05:00
Lance Edgar 3968e40a0b Add basic feature for "grid totals" 2023-09-15 19:19:20 -05:00
Lance Edgar ac6106ca69 Update changelog 2023-09-15 10:34:25 -05:00
Lance Edgar eed73eca81 Add get_rattail_app() method for view supplements 2023-09-14 12:56:15 -05:00
Lance Edgar 608da824d9 Tweak default field list for batch views 2023-09-13 13:14:00 -05:00
Lance Edgar 03fc301dec Update changelog 2023-09-12 18:31:18 -05:00
Lance Edgar 1cad8b2481 Show events instead of notes, in field subgrid for custorder item 2023-09-12 12:39:23 -05:00
Lance Edgar e930199f83 Avoid legacy logic for Customer.people schema 2023-09-11 17:13:07 -05:00
Lance Edgar 60044d5cdf Update changelog 2023-09-11 15:58:35 -05:00
Lance Edgar e793ba6630 Improve grids for custorder items
main grid as well as rows grid for Pending Product
2023-09-11 15:24:00 -05:00
Lance Edgar 67ec6f7773 Add support for "mark received" when viewing custorder item 2023-09-10 19:55:48 -05:00
Lance Edgar ddb8e3656f Add support for toggling custorder item "flagged" 2023-09-10 17:49:29 -05:00
Lance Edgar e49e0edc57 Misc. improvements for Customer Orders view 2023-09-10 17:34:54 -05:00
Lance Edgar e255c35e86 Set stacklevel for all deprecation warnings 2023-09-10 13:51:11 -05:00
Lance Edgar 48daa042d1 Show related customer orders for Pending Product view
and similar tweaks
2023-09-10 09:34:56 -05:00
Lance Edgar 64c58a3cf8 Optionally configure SQLAlchemy Session with future=True
this avoids the need for setting `cascade_backrefs=False` everywhere

https://docs.sqlalchemy.org/en/14/errors.html#error-s9r1

https://docs.sqlalchemy.org/en/14/orm/session_api.html#sqlalchemy.orm.Session.params.future
2023-09-10 07:44:13 -05:00
Lance Edgar a9fbf48053 Use common POST logic for submitting new customer order 2023-09-09 16:21:57 -05:00
Lance Edgar ccb4661b39 Add custom hook for grid "apply filters"
so a page can know when the data set changes..

this seems a bit hacky, may need a better solution some day
2023-09-09 14:14:23 -05:00
Lance Edgar c5344d2df6 Update changelog 2023-09-08 19:55:14 -05:00
Lance Edgar 669e50e406 Fix member key display for equity payment form 2023-09-08 19:53:10 -05:00
Lance Edgar 7221400b88 Fix msg body display, download link for email bounces 2023-09-08 10:56:25 -05:00
Lance Edgar 6e50288bd4 Add grid link for equity payment description 2023-09-08 08:49:43 -05:00
Lance Edgar 84de5e09a2 Update changelog 2023-09-07 21:00:40 -05:00
Lance Edgar f717bc47e5 Fallback to None when getting values for merge preview 2023-09-07 20:57:33 -05:00
Lance Edgar f732e04f49 Update changelog 2023-09-07 18:36:02 -05:00
Lance Edgar ecf46fa6fe Improve display for member equity payments 2023-09-07 18:19:46 -05:00
Lance Edgar b1ec1b8817 Update changelog 2023-09-02 13:56:10 -05:00
Lance Edgar bd7e6f9f8a Tweaks for cost editing within a receiving batch
never show PO Cost column in row grid, since Invoice Cost is what
receiving is most concerned with

add "zig-zag" entry behavior when both catalog and invoice costs are editable
2023-09-02 11:39:49 -05:00
Lance Edgar 75caface6b Add products API route to fetch label profiles for use w/ printing 2023-09-02 10:56:06 -05:00
Lance Edgar de373a683b Add grid filter type for BigInteger columns
so we can filter by larger values
2023-09-01 11:20:30 -05:00
Lance Edgar 5ab47aeead Update changelog 2023-08-31 10:08:20 -05:00
Lance Edgar 62aa0c5965 Preserve URL hash when redirecting in grid "reset to defaults" 2023-08-30 23:51:18 -05:00
Lance Edgar 625982d639 Avoid deprecated User.email_address property 2023-08-30 23:32:09 -05:00
Lance Edgar 9f65de2ba6 Update changelog 2023-08-30 22:08:50 -05:00
Lance Edgar f4267737c3 Let "new product" batch override type-2 UPC lookup behavior 2023-08-30 20:10:10 -05:00
Lance Edgar 74678882ee Update changelog 2023-08-29 22:21:20 -05:00
Lance Edgar 4e2125d613 Add support for "missing" credit in mobile receiving 2023-08-29 16:10:14 -05:00
Lance Edgar 12e4779093 Fairly massive overhaul of the Profile view; standardize tabs etc.
much cleaner and more consistent interface now, between the main
ProfileInfo component, and various *Tab components

also cleaner interface between client-side JS and server view methods

to my knowledge this is complete and breaks nothing..we'll see!
2023-08-28 20:43:31 -05:00
Lance Edgar 844c629a6a Fix profile history to show when a CustomerShopperHistory is deleted 2023-08-25 13:59:58 -05:00
Lance Edgar a40b44b6e3 Fix profile history to show when a CustomerShopperHistory is deleted 2023-08-25 10:41:20 -05:00
Lance Edgar bc8b5a8d32 Link to product record, for New Product batch row
also fix a typo
2023-08-25 09:08:33 -05:00
Lance Edgar 8be7dac33b Include shopper history from parent customer account perspective
..right?  or should this be hidden? configurable etc.?
2023-08-24 22:00:11 -05:00
Lance Edgar b2aea57da6 Auto-select text when editing costs for receiving 2023-08-18 15:04:52 -05:00
Lance Edgar a007606863 Declare "from PO" receiving workflow if applicable, in API 2023-08-17 18:12:42 -05:00
Lance Edgar 90075b3b65 When bulk-deleting, skip objects which are not "deletable"
whatever that means in context
2023-08-09 18:04:51 -05:00
Lance Edgar 4ecea891b3 Update changelog 2023-08-08 18:42:50 -05:00
Lance Edgar 845b5cda1a Fix custom cell click handlers in main buefy grid tables
just used for editing catalog/invoice cost in receiving thus far..
2023-08-08 18:06:22 -05:00
Lance Edgar f2915afda4 Fix HTML rendering for UOM choice options
also avoid deprecated config methods
2023-08-08 14:11:54 -05:00
Lance Edgar d504da19c5 Add common logic to validate employee reference field 2023-08-07 12:36:07 -05:00
Lance Edgar ec7b0cdda1 Update changelog 2023-08-03 22:42:34 -05:00
Lance Edgar 9f0cfc68c1 Make system key searchable for problem report grid 2023-08-02 21:59:52 -05:00
Lance Edgar 1f3b5a49c4 Update changelog 2023-07-15 19:32:04 -05:00
Lance Edgar a84bcf688b Tweak display options for tempmon probe readings graph 2023-07-07 17:56:45 -05:00
Lance Edgar 4729785b05 Show invoice number for each row in receiving 2023-07-07 17:19:08 -05:00
Lance Edgar 6b6e358dbe Update changelog 2023-07-07 15:38:08 -05:00
Lance Edgar 58f9b3ce2a Optimize "auto-receive" batch process
disable versioning when doing "auto-receive" for a receiving batch
2023-07-06 21:23:44 -05:00
Lance Edgar 8742a03e18 Update changelog 2023-07-03 09:52:42 -05:00
Lance Edgar 1be26b7f33 Allow "arbitrary" PO attachment to purchase batch
for sake of other POS integration etc.
2023-06-27 18:18:35 -05:00
Lance Edgar 08a75f6e9f Avoid deprecated product key field getter 2023-06-27 12:37:00 -05:00
Lance Edgar 8cc6def93e Update changelog 2023-06-20 17:06:54 -05:00
Lance Edgar 70ee784818 Include user "active" flag in profile view context
whoops, missed that one..
2023-06-20 17:06:20 -05:00
Lance Edgar 8932b51216 Update changelog 2023-06-20 11:54:09 -05:00
Lance Edgar 69bda79baf Turn on quickie person search for CustomerShopper views
also set default sort for that grid
2023-06-18 21:20:45 -05:00
Lance Edgar 214f3d9b1e Improve merge support for records with no uuid
for now we "pretend" they have a uuid still, custom view is
responsible for determining the value for each row if needed
2023-06-18 21:20:45 -05:00
Lance Edgar 58354e7adf Add views etc. for member equity payments 2023-06-18 21:20:40 -05:00
Lance Edgar aa5e44efb5 Update changelog 2023-06-17 18:12:30 -05:00
Lance Edgar 9572fbf584 Fix some things for viewing a member 2023-06-17 16:56:40 -05:00
Lance Edgar b6cb119e89 Remove unwanted revisions for CustomerPerson etc. 2023-06-17 16:50:39 -05:00
Lance Edgar 12eeb5df97 Add basic support for Person quickie lookup
shows profile view if person is found
2023-06-17 16:09:24 -05:00
Lance Edgar d77de76c97 Add support for Notes tab in profile view 2023-06-17 14:24:08 -05:00
Lance Edgar 105dab7a3d Tweak SimpleRequestMixin to not rely on response.data.ok
instead just assume ok unless `response.data.error` is set
2023-06-17 14:13:37 -05:00
Lance Edgar ba2b4bf12c Cleanup some wording in profile view template 2023-06-17 02:27:17 -05:00
Lance Edgar b1489c56e2 Add basic Shopper tab for profile view 2023-06-17 02:22:18 -05:00
Lance Edgar c601d46970 Update changelog 2023-06-16 22:22:03 -05:00
Lance Edgar 51cad13f5a Update usage of app handler per upstream changes 2023-06-16 22:15:52 -05:00
Lance Edgar 17ae06f9c1 Update changelog 2023-06-16 20:43:00 -05:00
Lance Edgar 5a03f5c23e Join the Person model for Customers grid differently based on config 2023-06-16 20:08:27 -05:00
Lance Edgar bf1726a52b Add users context data for profile view
instead of using server-side data/logic for users tab
2023-06-16 17:04:39 -05:00
Lance Edgar c1f72e0d11 Fix grid filter bug when switching from 'equal' to 'between' verbs
and vice versa
2023-06-16 12:21:51 -05:00
Lance Edgar c2227b306b Update changelog 2023-06-15 10:47:38 -05:00
Lance Edgar 961cf803f2 Prefer account holder, shoppers over legacy Customers.people
but until all are migrated, support both
2023-06-14 23:33:05 -05:00
Lance Edgar eab3b75ae5 Update changelog 2023-06-12 20:35:00 -05:00
Lance Edgar 92538b87ad Add master view for CustomerShopper 2023-06-11 20:52:24 -05:00
Lance Edgar 5f4d393db3 Change label for Member.person to "Account Holder"
probably should rename table column etc. too but that can wait
2023-06-11 15:42:14 -05:00
Lance Edgar edd5d49e36 Improve shoppers/people display for Customer tab in profile view
also expose settings for people/clientele handlers
2023-06-11 14:52:07 -05:00
Lance Edgar 0d52d554e7 Add options for grid results to link straight to Profile view
probably should have done this a long time ago...
2023-06-10 23:19:52 -05:00
Lance Edgar f1a8b8df7f Include version history for CustomerShopper, in profile view 2023-06-10 21:09:35 -05:00
Lance Edgar 9e1b83cbbe Let external customer link buttons be more dynamic, for profile view
need to copy this pattern elsewhere yet i'm sure..
2023-06-10 20:12:33 -05:00
Lance Edgar 40ae14bd7a Consider vendor catalog batch views "typical" 2023-06-10 18:59:53 -05:00
Lance Edgar e2b91dca23 Move "view history" and related buttons, for person profile view
need those to be more front-and-center
2023-06-10 14:22:21 -05:00
Lance Edgar 3fde80f991 Add basic support for exposing Customer.shoppers
now there is a Shoppers field when viewing a Customer, unless
configured otherwise

also tweaked some logic for navigating Customer/Person relationships,
to handle implications of Shoppers being (maybe) present
2023-06-07 20:57:30 -05:00
Lance Edgar afd5c3a5fd Update changelog 2023-06-06 19:29:47 -05:00
Lance Edgar cfdb492349 Add support for version history in person profile view
yay, finally
2023-06-06 16:37:58 -05:00
Lance Edgar 816e652357 Add basic support for membership types 2023-06-06 13:13:19 -05:00
Lance Edgar 027d44e04a Remove old/unused feedback templates 2023-06-06 11:57:20 -05:00
Lance Edgar c38dc8b842 Use *actual* current URL for user feedback msg
was using current URL as of page load, but #hash can change after
that, e.g. on profile view
2023-06-06 11:54:58 -05:00
Lance Edgar 0d97ff2936 Add support for "configured customer/member key"
also improve product key support, same patterns
2023-06-06 11:36:19 -05:00
Lance Edgar 9b59b44609 Add "touch" support for Members 2023-06-06 09:40:14 -05:00
Lance Edgar 6f02e1b18e Tweak logic for MasterView.get_action_route_kwargs()
hopefully this improves default handling when model keys are
composite, and if we can confirm the "secondary" (previous) logic no
longer happens, then can remove that altogether..?
2023-06-06 09:39:02 -05:00
Lance Edgar 488126b92c Add customer number filter for People grid 2023-06-05 20:18:57 -05:00
Lance Edgar 4318f03bd6 Add "typical" view config, for e.g. Theo and the like
bring in all normal views for backoffice retail
2023-06-05 20:18:40 -05:00
Lance Edgar 13ac33bb27 Update changelog 2023-06-02 14:19:53 -05:00
Lance Edgar 93b03c9562 Expose mail handler and template paths in email config page 2023-06-02 14:14:33 -05:00
Lance Edgar e1685231c2 Update changelog 2023-06-01 12:17:19 -05:00
Lance Edgar 90cb25446b Fix datasync consumer setting save logic 2023-06-01 11:37:26 -05:00
Lance Edgar fd2b290fd0 Save datasync config with new keys, per RattailConfiguration 2023-06-01 11:12:31 -05:00
Lance Edgar b4816c6289 Share some code for validating vendor field
and add validation for new Ordering batch
2023-05-30 13:25:20 -05:00
Lance Edgar 0d9a502801 Fix test for config object 2023-05-25 14:55:41 -05:00
Lance Edgar b840ae7513 Update changelog 2023-05-25 12:21:04 -05:00
Lance Edgar 29767dfcfb Define essential views for API 2023-05-19 19:46:18 -05:00
Lance Edgar dd3f91cf0c Tweak byjove project generator form 2023-05-19 19:45:41 -05:00
Lance Edgar ae38e09d1b Avoid error when filter params not valid 2023-05-19 17:43:31 -05:00
Lance Edgar de13e48aa5 Expose basic way to send test email
most of the mechanics of sending email could already be tested by
sending a "preview" email of any type, or e.g. via Feedback.  but it
seemed like the Configure Email Settings page should have a dedicated
way to test sending
2023-05-19 17:16:19 -05:00
Lance Edgar 05bb3849a2 Prevent bug in upgrade diff for empty new version
apparently this is quite the rare case..but can happen
2023-05-18 19:57:05 -05:00
Lance Edgar af405cfd10 Update changelog 2023-05-18 13:51:59 -05:00
Lance Edgar 8d880fc9dd Add workaround for "share grid link" on insecure sites 2023-05-18 13:48:22 -05:00
Lance Edgar c18367739f Add initial swagger.json endpoint for API
probably this needs more, but good enough to test with
2023-05-16 23:34:48 -05:00
Lance Edgar 26a6a4d991 Update changelog 2023-05-16 17:33:55 -05:00
Lance Edgar 93bce57888 Prevent error in old product search logic
when no POD image URL is configured
2023-05-16 17:33:07 -05:00
Lance Edgar 5f6b389556 Replace setup.py contents with setup.cfg 2023-05-16 15:02:39 -05:00
Lance Edgar d90cab40a6 Update changelog 2023-05-15 08:49:01 -05:00
Lance Edgar c002d3d182 Add basic support for managing, and accepting API tokens
also various other changes in pursuit of that.  so far tokens are only
accepted by web API and not traditional web app
2023-05-15 08:10:42 -05:00
Lance Edgar 85947878c4 Get rid of newstyle flag for Form.validate() method
we always/only use "new style" now
2023-05-15 08:10:42 -05:00
Lance Edgar a991dc0684 Update changelog 2023-05-13 16:57:36 -05:00
Lance Edgar 29817653ed Warn user if DB not up to date, in new table wizard
also start adding 'dirty' page behavior, to warn user if navigating
away that changes will be lost

also improve steps in wizard, so page header is scrolled into view
when prev/next buttons are clicked.  unfortunately it still does not
work right if user clicks the step number on left of screen..
2023-05-12 21:27:15 -05:00
Lance Edgar f5f973dc3a Tweak button wording in "find role by perm" form 2023-05-12 19:21:48 -05:00
Lance Edgar f49b4d1b8b Update changelog 2023-05-10 20:20:30 -05:00
Lance Edgar 82656f263d Move row delete check logic for receiving to batch handler 2023-05-10 18:47:11 -05:00
Lance Edgar f942716bf9 Update changelog 2023-05-09 20:31:43 -05:00
Lance Edgar dcc7819466 Misc. tweaks for "run import job" form 2023-05-09 20:25:05 -05:00
Lance Edgar 8fcef1fb4d Add form config for generating 'shopfoo' projects 2023-05-09 15:30:23 -05:00
Lance Edgar 2f5e01c9e9 Update changelog 2023-05-05 19:10:54 -05:00
Lance Edgar 50d1bbbe4d Add "rattail-adjacent" logic for generating projects 2023-05-05 13:30:32 -05:00
Lance Edgar 62bdf82627 Include project views by default, in "essential" views 2023-05-05 10:39:29 -05:00
Lance Edgar 2ed63b1c1a Massive overhaul of "generate project" feature
previous incarnation was woefully lacking.  new feature is much more
extensible.  still need to remove old POS integration specifics in
some places.

and a couple of unrelated things that snuck in..

- deprecate `rattail.util.OrderedDict`
- deprecate `rattail.util.import_module_path()`
- deprecate `rattail.util.import_reload()`
2023-05-05 00:18:16 -05:00
Lance Edgar 026d98551c Update changelog 2023-05-03 10:55:15 -05:00
Lance Edgar f913ed8332 Expose, honor the prevent_password_change flag for Users 2023-05-02 19:13:28 -05:00
Lance Edgar 2863ff7a5c Remove references to deprecated extra in tox.ini 2023-04-27 09:22:48 -05:00
Lance Edgar 4993b349ef Avoid error if tempmon probe has invalid status 2023-04-21 12:04:36 -05:00
Lance Edgar eb31fa9ab7 Update changelog 2023-04-17 16:10:37 -05:00
Lance Edgar 18f8577005 Improve global menu search behavior for multiple terms 2023-03-31 14:02:09 -05:00
Lance Edgar 6ab3898f27 Allow bulk-delete for products grid 2023-03-31 12:55:05 -05:00
Lance Edgar efb8f8f315 Update changelog 2023-03-27 12:53:16 -05:00
Lance Edgar e96f8844e2 Overhaul the "find by perm" feature a bit
use GET instead of POST on form submit, so can more easily share URL
for a particular result

also get rid of WTForms dependency!  sheesh

results table is still not pretty but..feeling lazy
2023-03-25 13:03:47 -05:00
Lance Edgar 45b8d9fb84 Fix table sorting for FK reference column in new table wizard
also add LargeBinary data type option
2023-03-25 11:34:30 -05:00
Lance Edgar 2f8411ba2f Add has_perm() etc. to request during the NewRequest event
still get the occasional server error when handling what should be a
simple 404 request e.g. for /wp-login.php

error indicates there is no `request.has_perm()` at the time, so
hoping this moves it earlier in the life cycle so it *will* exist..
2023-03-25 01:03:49 -05:00
Lance Edgar 714c0a6cfd Avoid accidental auto-submit of new msg form, for subject field 2023-03-23 10:23:19 -05:00
Lance Edgar 9125d7ef74 Update changelog 2023-03-15 09:43:21 -05:00
Lance Edgar 1ce67953df Let providers do DB connection setup for web API 2023-03-15 09:33:20 -05:00
Lance Edgar e19adf8907 Remove version workaround for sphinx
no longer needed
2023-03-09 15:26:34 -06:00
Lance Edgar 9ee46107d2 Update changelog 2023-03-09 14:10:31 -06:00
Lance Edgar 2ebe0401c3 Fix JSON rendering for Cornice API views
also make sure we use Cornice for all API views
2023-03-09 14:07:10 -06:00
Lance Edgar 5aa982c95f Update changelog 2023-03-08 20:39:39 -06:00
Lance Edgar 46c7ef42de Remove version cap for cornice, now that we require python3 2023-03-08 20:38:16 -06:00
Lance Edgar a9c4d37819 Update changelog 2023-03-02 11:05:20 -06:00
Lance Edgar e8f235e4f7 Allow download results for Trainwreck
just basic transaction headers so far..
2023-02-28 15:05:38 -06:00
Lance Edgar ad311e9e7e Add "equal to any of" verb for string-type grid filters 2023-02-28 14:30:25 -06:00
Lance Edgar 01af73502a Update changelog 2023-02-24 20:04:14 -06:00
Lance Edgar a81e121ffd Allow sort/filter by vendor for sample files grid 2023-02-22 22:41:12 -06:00
Lance Edgar cf7e3c2302 Update changelog 2023-02-22 22:00:36 -06:00
Lance Edgar 743a2ccd07 Add views for sample vendor files 2023-02-22 22:00:05 -06:00
Lance Edgar e77650c997 Update changelog 2023-02-21 19:14:19 -06:00
Lance Edgar d1fc5d5c38 Validate vendor for catalog batch upload 2023-02-21 17:35:47 -06:00
Lance Edgar 2fa62acbbd Update changelog 2023-02-20 21:50:44 -06:00
Lance Edgar ad4ec41e15 Make config param more explicit, for GridFilter constructor
i.e. the rattail config object
2023-02-14 17:32:04 -06:00
Lance Edgar 539f4a5c31 Update changelog 2023-02-14 16:07:23 -06:00
Lance Edgar 7b2faf90f2 Add dedicated view config methods for "view" and "edit help"
so they can be invoked explicitly from elsewhere, keeping same logic

cf. Catapult Worksheets
2023-02-13 20:29:59 -06:00
Lance Edgar ac57ddbb16 Update changelog 2023-02-12 10:04:27 -06:00
Lance Edgar b434fa108d More refactoring, Query.get() => Session.get() 2023-02-12 09:34:38 -06:00
Lance Edgar f1496c771e Stop running tests for python 3.5; do run for 3.6, 3.9 2023-02-12 09:29:30 -06:00
Lance Edgar f611a5a521 Refactor Query.get() => Session.get() per SQLAlchemy 1.4 2023-02-11 22:05:45 -06:00
Lance Edgar 81aa0ae109 Update changelog 2023-02-11 11:55:43 -06:00
Lance Edgar 5736faf24c Use sa-filters instead of sqlalchemy-filters for API queries
latter was abandoned it seems; former has support for SQLAlchemy 1.4
and looks to be a drop-in replacement

another option, if needed at some point, though i like the looks of it
less, is https://sqlalchemy-filters-plus.readthedocs.io/

see also:

https://github.com/juliotrigo/sqlalchemy-filters/pull/69
https://github.com/juliotrigo/sqlalchemy-filters/issues/72
2023-02-11 11:53:47 -06:00
Lance Edgar c87c50bfb9 Update changelog 2023-02-11 09:59:45 -06:00
Lance Edgar 10162b378a Remove legacy grid for alt codes in product view
whoops missed this in jquery purge
2023-02-10 21:23:57 -06:00
Lance Edgar e879102768 Remove python2 stuff from tox.ini 2023-02-10 20:42:36 -06:00
Lance Edgar de4667cc71 Update changelog 2023-02-10 20:25:02 -06:00
Lance Edgar 8fc3a71e0f Fix multi-file upload widget bug
happened when only one file was being uploaded
2023-02-10 12:40:23 -06:00
Lance Edgar 2d2c94e4d7 Expose setting for POD image URL 2023-02-10 12:21:55 -06:00
Lance Edgar 21669b5f4a Remove legacy vendor sources grid for product view
whoops, missed that when purging jquery theme
2023-02-10 11:39:10 -06:00
Lance Edgar ad5dec3dc6 Use label handler to avoid deprecated logic 2023-02-08 20:19:15 -06:00
Lance Edgar 32fc0415da Fix auto-advance on ENTER for login form
if user hits ENTER while focused on username field, just set focus to
password field but do not submit form.  if user hits ENTER on while
the password field is focused, then submit form

this has long been the behavior but it was broken when removing jquery
2023-02-07 16:13:07 -06:00
Lance Edgar 5f70a524e9 Use latest zope.sqlalchemy package
session / transaction registration modified per upstream changes, but
previous logic kept to support older versions of zope.sqlalchemy - for
now, although probably should require minimum version soon?
2023-02-07 12:20:22 -06:00
Lance Edgar 9263dd4ddb Add dependency for pyramid_retry 2023-02-04 19:03:05 -06:00
Lance Edgar f17ff59ba6 Update changelog 2023-02-03 19:52:26 -06:00
Lance Edgar 15fb7f45b8 Fix auto-focus username for login form 2023-02-03 19:51:50 -06:00
Lance Edgar f71eadd409 Update changelog 2023-02-03 18:07:50 -06:00
Lance Edgar 49122d940d Stop including deform JS static files
although maybe we *should* be using that method, for some things?  can
revisit later if desired
2023-02-03 18:06:40 -06:00
Lance Edgar eb1351d108 Update changelog 2023-02-03 17:39:28 -06:00
Lance Edgar b67df1328b Remove liburl logic, config for jquery 2023-02-03 17:32:39 -06:00
Lance Edgar 976a5836a9 Purge even more jquery stuff
and related static files etc. from old themes

this might be the end of it..??
2023-02-03 17:08:33 -06:00
Lance Edgar 2ebae17839 Refactor the Ordering Worksheet generator, per Buefy 2023-02-03 16:10:08 -06:00
Lance Edgar eddbfcab36 Allow editing the Department field for a Subdepartment 2023-02-03 16:10:08 -06:00
Lance Edgar 320aaab4b3 Replace 'default' theme to match 'falafel'
falafel is now an empty wrapper around default

hell yeah
2023-02-03 16:10:08 -06:00
Lance Edgar f0880785a9 Add new Buefy-specific upgrade template
since that was broken..
2023-02-03 16:10:08 -06:00
Lance Edgar 9faaea881d Remove all deprecated use_buefy logic
also remove some static files no longer used, etc.
2023-02-03 16:10:04 -06:00
Lance Edgar 01e5eee981 Officially drop support for python2 2023-02-02 23:21:45 -06:00
Lance Edgar 94a0a57cfe Update changelog 2023-02-02 22:45:58 -06:00
Lance Edgar 265c7ad76f Always assume use_buefy=True within main page template
so can start removing from context for various views
2023-02-02 21:18:00 -06:00
Lance Edgar 36a902398a Update changelog 2023-02-02 20:24:19 -06:00
Lance Edgar 506de0383f Form constructor assumes use_buefy=True by default
until we get rid of it altogether
2023-02-02 20:21:19 -06:00
Lance Edgar 9b67010f2c Fix checkbox behavior for Inventory Worksheet 2023-02-02 19:26:47 -06:00
Lance Edgar f7f8f8dabf Update changelog 2023-02-02 16:51:12 -06:00
Lance Edgar 01182ef752 Add progress bar page for Buefy theme 2023-02-01 23:09:33 -06:00
Lance Edgar 8410419717 Remove support for Buefy 0.8
only Buefy 0.9 and greater are supported now
2023-02-01 18:44:55 -06:00
Lance Edgar 5f7fa33eb2 Update changelog 2023-01-30 21:06:08 -06:00
Lance Edgar a1d88a5e6b Refactor the Inventory Worksheet generator, per Buefy 2023-01-30 11:56:09 -06:00
Lance Edgar a3723e4879 Tweak the Ordering Worksheet generator, per Buefy 2023-01-30 11:46:07 -06:00
Lance Edgar b7f3a67cd0 Add basic API support for printing product labels 2023-01-29 18:46:49 -06:00
Lance Edgar c880065da8 Tweak customer panel header style for new custorder 2023-01-29 13:02:39 -06:00
Lance Edgar 86af4baef5 Fix icon for multi-file upload widget 2023-01-29 12:45:14 -06:00
Lance Edgar 8cdfe4a22c Update changelog 2023-01-28 16:22:54 -06:00
Lance Edgar d6f05684be Tweak styles for Quantity panel when viewing Receiving row
when no buttons were visible in panel, right-hand side looked "cut off"
2023-01-28 16:12:03 -06:00
Lance Edgar 17251b2c88 Tweak import handler form, some fields not required
those particular fields are for read-only display, not meant for user
to provide values.  so must provide defaults, else form missing those
will not validate.
2023-01-28 15:54:53 -06:00
Lance Edgar 64acfbcb4e Update changelog 2023-01-26 13:36:14 -06:00
Lance Edgar 55a3f9669b Fix click event for right-aligned buttons on profile view
for some reason when `is-pulled-right` was used, buttons were not
clickable?!  never did figure out precisely why, but this fixes
anyway.  was not an issue w/ buefy 0.8 fwiw, but using 0.9 now
2023-01-26 13:34:13 -06:00
Lance Edgar 884f136e99 Update changelog 2023-01-18 22:04:35 -06:00
Lance Edgar dc6bd4d4a7 Rename frontend request handler logic to SimpleRequestMixin 2023-01-18 21:56:29 -06:00
Lance Edgar 1e5b7e7ee7 Add a couple more menu items to default set 2023-01-18 21:54:24 -06:00
Lance Edgar c874d97507 Add default view config for Trainwreck 2023-01-18 20:11:46 -06:00
Lance Edgar 3f61c9ee18 Add some more menu items to default set 2023-01-18 19:21:34 -06:00
Lance Edgar eece358e20 Update changelog 2023-01-18 18:58:32 -06:00
Lance Edgar 2b1fd9e986 Add way to override particular 'essential' views 2023-01-18 18:41:23 -06:00
Lance Edgar 79e4e596e8 Include permission views by default 2023-01-18 17:58:04 -06:00
Lance Edgar 23dea7bced Add more views, menus to default set 2023-01-18 16:55:30 -06:00
Lance Edgar e4c2336659 Add specific data type options for new table entry form
including basic FK / relationship support
2023-01-17 00:00:06 -06:00
Lance Edgar 98fa6eea05 Misc. tweaks for App Details / Configure Menus 2023-01-16 21:55:52 -06:00
Lance Edgar 9b21d52206 Update changelog 2023-01-16 18:44:54 -06:00
Lance Edgar 00548a259b Add basic "new model view" wizard 2023-01-16 13:50:27 -06:00
Lance Edgar f4bc280da7 Wrap up steps for new table wizard
it actually works.. :)  needs more polish, but will let usage drive that
2023-01-15 22:52:01 -06:00
Lance Edgar 68ed5942e6 Add basic "Review Model" step for new table wizard 2023-01-14 23:23:21 -06:00
Lance Edgar 9d2bcff96b Add full set of default menus
plus dynamic set of integration menus, from providers
2023-01-14 18:48:56 -06:00
Lance Edgar 39d53617bd Add new handlers, TailboneHandler and MenuHandler 2023-01-14 16:01:26 -06:00
Lance Edgar cfdaa1e927 Add default logic to get merge data for object 2023-01-14 12:17:05 -06:00
Lance Edgar aef679c030 Fix bug when adding new profile via datasync configure 2023-01-14 11:51:22 -06:00
Lance Edgar dec0ebba30 Let the API "rawbytes" response be just that, w/ no file 2023-01-14 10:31:31 -06:00
Lance Edgar e82e27acd7 Update changelog 2023-01-14 08:40:08 -06:00
Lance Edgar 23358d9c5d Tweak how backfill task is launched
per upstream changes
2023-01-14 02:20:21 -06:00
Lance Edgar 80989cc84f Update changelog 2023-01-13 20:53:26 -06:00
Lance Edgar d8bd4bd847 Prevent listing for top-level Messages view
user must access inbox, archive etc. directly instead
2023-01-13 20:28:00 -06:00
Lance Edgar f18f24962e Refactor tempmon dashboard view, for buefy themes 2023-01-13 20:18:42 -06:00
Lance Edgar 0753e956f9 Revert logic that assumes all themes use buefy
that just isn't a safe assumption yet..alas
2023-01-13 18:10:28 -06:00
Lance Edgar 83f9a3faa7 Fix "toggle batch complete" for Chrome browser 2023-01-13 16:49:16 -06:00
Lance Edgar cac005f993 Semi-finish logic for writing new table model class to file
definitely needs more polish and features, but the gist..
2023-01-13 03:51:12 -06:00
Lance Edgar fb7368993c Show basic column info as row grid when viewing Table 2023-01-12 22:56:12 -06:00
Lance Edgar 38f88407ff Update changelog 2023-01-12 15:33:56 -06:00
Lance Edgar d842a3d8e0 Add new views for App Info, and Configure App
and a way to specify version/url overrides for buefy, vue etc.

also, begin logic for "standard" admin menu
2023-01-12 15:30:10 -06:00
Lance Edgar 2163522e7c Update changelog 2023-01-11 23:31:09 -06:00
Lance Edgar 225e13f43b Allow all external dependency URLs to be set in config
so can host all files locally if needed.

we also now assume all themes support buefy unless otherwise
configured
2023-01-11 23:29:28 -06:00
Lance Edgar fa1cf353b8 Update changelog 2023-01-11 19:55:52 -06:00
Lance Edgar 4746b6fae9 Refactor inventory batch "add row" page, per new theme 2023-01-11 19:31:14 -06:00
Lance Edgar 2c7f2c0fcd Fix panel header icon behavior for new custorder
had to work around a buefy bug..?
2023-01-11 16:41:11 -06:00
Lance Edgar b8389c72bb Add support for per-item default discount, for new custorder 2023-01-11 16:41:07 -06:00
Lance Edgar dfa4178204 Add basic support for receiving from multiple invoice files 2023-01-10 16:46:21 -06:00
Lance Edgar 2b7ebedb22 Update changelog 2023-01-08 11:36:42 -06:00
Lance Edgar 33ffd7e855 Improve "download rows as XLSX" for importer batch
still could be better, but at least this avoids error
2023-01-07 22:46:35 -06:00
Lance Edgar b11f9f62b7 Update changelog 2023-01-07 11:53:10 -06:00
Lance Edgar c6765fd9a9 Expose, start to honor "units only" setting for products 2023-01-07 11:52:37 -06:00
Lance Edgar 8c201dced7 Update changelog 2023-01-05 13:43:38 -06:00
Lance Edgar 71851e1a05 Use product handler to normalize data for products API
at least, as much as possible
2023-01-04 21:23:57 -06:00
Lance Edgar db62bd20b3 Show help link when generating or viewing report, if applicable 2023-01-04 16:39:37 -06:00
Lance Edgar 31b213610f Fix template bug for generating report 2023-01-04 15:31:51 -06:00
Lance Edgar d0881cbd09 Keep aspect ratio for product images in new custorder 2023-01-04 12:38:04 -06:00
Lance Edgar 7e4bd851f1 Update changelog 2023-01-04 10:57:14 -06:00
Lance Edgar ab80aedb63 Allow xref buttons to have "internal" links
still assume external (open in new tab) by default
2023-01-04 00:09:35 -06:00
Lance Edgar c7537e7994 Update changelog 2023-01-02 16:55:39 -06:00
Lance Edgar 9f763b46eb Expose some settings for Trainwreck DB rotation 2023-01-02 13:12:01 -06:00
Lance Edgar d21826c70d Make invalid email more obvious, in profile view 2023-01-02 11:11:01 -06:00
Lance Edgar a061e362c3 Add beginnings of "New Table" feature
nowhere near complete yet, but skeleton is more or less in place
2023-01-02 09:44:05 -06:00
Lance Edgar 7e852c1836 Allow buefy version to be 'latest' 2023-01-01 13:17:55 -06:00
Lance Edgar a01982ae55 Show only "core" app settings by default 2022-12-31 17:57:22 -06:00
Lance Edgar 884f960d3b Update changelog 2022-12-28 16:12:33 -06:00
Lance Edgar 0c6bfcbee6 Use minified version of vue.js by default, in falafel theme 2022-12-28 14:40:50 -06:00
Lance Edgar 03639d73fa Show global search as button instead of link 2022-12-27 22:51:42 -06:00
Lance Edgar cfc92ac9e7 Hide the "configure field help" icons until user requests access
user can technically "request access" on "any page" and not just those
with configurable fields..but who cares for now i think..
2022-12-27 22:30:25 -06:00
Lance Edgar dc90abcf09 Add "global searchbox" for quicker access to main views 2022-12-26 17:31:37 -06:00
Lance Edgar b985124bef Fix product image view for python3 2022-12-26 10:33:12 -06:00
Lance Edgar b653351f71 Avoid error when no form present 2022-12-25 23:05:53 -06:00
Lance Edgar 0a0b471a03 Add support for websockets over HTTP
in addition to HTTPS
2022-12-25 15:37:54 -06:00
Lance Edgar c389ebabd0 Show *correct* system title when upgrading
may not be the same as primary app title
2022-12-25 15:13:59 -06:00
Lance Edgar 8264a69cec Add "direct link" support for master grids 2022-12-25 14:42:29 -06:00
Lance Edgar cd466a64e5 Filter by person instead of user, for Generated Reports "Created by" 2022-12-25 12:45:23 -06:00
Lance Edgar b04c1054fc Override document title when upgrading
when using websockets, to mimic old behavior without them
2022-12-25 12:25:55 -06:00
Lance Edgar 3befdc09e3 Add basic support for editing field help info 2022-12-24 21:46:02 -06:00
Lance Edgar 9fe9983bf9 Add basic support for editing page help info
site admin should be able to point help wherever they want
2022-12-24 16:08:09 -06:00
Lance Edgar ed54092268 Add cleanup logic for old Beaker session data
pretty basic, but good enough for now
2022-12-23 23:30:45 -06:00
Lance Edgar 50dafc91d4 Preserve current tab for page reload in profile view
also makes sharing links better etc.
2022-12-23 20:58:27 -06:00
Lance Edgar d409e1d088 Make static files optional, for new tailbone-integration project 2022-12-23 20:18:49 -06:00
Lance Edgar 64c8768314 Fix checkbox values when re-running a report 2022-12-23 19:43:31 -06:00
Lance Edgar c5bd40793b Fix HUD display when toggling employee status in profile view 2022-12-23 19:06:05 -06:00
Lance Edgar 8a6fdb5ea5 Warn user when luigi is not installed, for relevant view
better than getting a server error
2022-12-23 18:55:53 -06:00
Lance Edgar 6fbc79fe5e Add support for Buefy 0.9.x
or: add hacks to continue supporting Buefy 0.8.x

..depending on your perspective
2022-12-22 20:49:20 -06:00
Lance Edgar 7ccd9ad896 Update changelog 2022-12-21 20:01:31 -06:00
Lance Edgar ef9dc9ff6d Expose the terms field for Vendor CRUD 2022-12-21 18:05:38 -06:00
Lance Edgar ed0a1f2740 Add make_status_renderer() to MasterView
batches aren't the only table/view where a status code/text combo may
be in use
2022-12-20 19:15:31 -06:00
Lance Edgar 871ea84f96 Add support for "is row checkable" in grids
i.e. when grid has checkboxes, some rows maybe shouldn't get one
2022-12-20 19:14:54 -06:00
Lance Edgar e427e50d67 Update changelog 2022-12-15 13:32:27 -06:00
Lance Edgar 99a5615e91 Add configure_execute_form() hook for batch views
also enable bulk-delete of row results by default for batch views
2022-12-15 09:12:26 -06:00
Lance Edgar c8201de2ff Update changelog 2022-12-10 12:41:41 -06:00
Lance Edgar 3c54960612 Fix error if no view supplements defined 2022-12-10 12:41:10 -06:00
Lance Edgar 5045df0b57 Update changelog 2022-12-10 11:35:02 -06:00
Lance Edgar f388f84b07 Suppress error if menu entry has bad route name 2022-12-10 10:09:39 -06:00
Lance Edgar f8f6b76657 Add xref buttons for Customer, Member tabs in profile view 2022-12-10 09:48:22 -06:00
Lance Edgar cb6c25f829 Let view supps give data instead of actual xref button 2022-12-10 09:48:22 -06:00
Lance Edgar 05a3e3f805 Add common logic to determine panel fields for product view
so we don't have to override templates, but just the view logic

more needed, but this proves the concept
2022-12-10 09:48:22 -06:00
Lance Edgar 273fa7eb55 Add common logic for xref buttons, links when viewing object
about dang time for this..probaby needs improvement but a good start
2022-12-10 09:48:22 -06:00
Lance Edgar 2278082a4d Cleanup employees view per new supplements
also add permission for "view employee secrets" (where applicable)
2022-12-10 09:48:22 -06:00
Lance Edgar d5d9c644a2 Add the ViewSupplement concept
also fix cell-class for grid columns.  cannot use "raw" fieldname
because in some cases (e.g. 'number', 'rate') Bulma may interpret that
as actually meaning something, and affect the display
2022-12-10 09:46:54 -06:00
Lance Edgar 1a51f3d854 Fix ordering worksheet API for date objects 2022-12-08 14:54:36 -06:00
Lance Edgar f80d3cd530 Show simple error string, when subprocess batch actions fail
logs still have more info, can't show user the whole traceback..but
this is better than we had before..
2022-12-08 14:15:38 -06:00
Lance Edgar cea06c9673 Update changelog 2022-12-07 14:20:04 -06:00
Lance Edgar 22176e89dd Add support for Beaker >= 1.12.0
but still support previous versions too, for now
2022-12-07 14:00:32 -06:00
Lance Edgar 2b220459c7 Temporary cap version for Beaker, per broken web apps! 2022-12-07 12:33:32 -06:00
Lance Edgar c1b2b7e177 Update changelog 2022-12-06 19:32:10 -06:00
Lance Edgar 6ac07e1255 Fix bug when viewing certain receiving batches 2022-12-06 19:31:22 -06:00
Lance Edgar 1509b6fce5 Update changelog 2022-12-06 10:33:57 -06:00
Lance Edgar ebe2013849 Add helptext for "Admin-ish" field when editing Role 2022-12-06 10:30:30 -06:00
Lance Edgar cceb66e500 Add support for editing invoice cost in receiving batch, per new theme 2022-12-05 16:25:55 -06:00
Lance Edgar 36a5f2ab49 Show invoice cost in receiving batch, if "from scratch" 2022-12-05 16:05:27 -06:00
Lance Edgar 9c54a4ada1 Add receiving workflow as param when making receiving batch 2022-12-05 15:26:43 -06:00
Lance Edgar 2e3823364c Add support for editing catalog cost in receiving batch, per new theme
had to add several "under the hood" features to make this work, to
embed a Vue component within grid `<td>` cells, etc.
2022-12-05 14:03:03 -06:00
Lance Edgar ec71f532a1 Include email address for current API user info 2022-12-04 09:39:08 -06:00
Lance Edgar 4030904d40 Add simple template hook for "before object helpers"
not sure how useful, but needed in one place, and hook makes for
cleaner template inheritance
2022-12-02 12:16:51 -06:00
Lance Edgar 9f62c280de Update changelog 2022-12-01 13:14:17 -06:00
Lance Edgar 94fa0380ba Avoid web config when launching overnight task 2022-12-01 09:37:30 -06:00
Lance Edgar b3bdee60bb Add way to quickly re-run "any" report 2022-11-28 17:55:08 -06:00
Lance Edgar 434633924a Update changelog 2022-11-28 10:54:37 -06:00
Lance Edgar 88aeaf31c2 Show "next date" when launching overnight task
just to make things a bit more clear
2022-11-27 14:55:49 -06:00
Lance Edgar 604420c7d4 Auto-format phone number when saving for contact records 2022-11-23 12:27:09 -06:00
Lance Edgar b64f6c7884 Use newer config strategy for all views
to make inheritance easier
2022-11-23 12:20:58 -06:00
Lance Edgar db9b3617a4 Fix page title for datasync status 2022-11-23 11:52:44 -06:00
Lance Edgar 42888c0983 Add prompt dialog when launching overnight task 2022-11-23 11:40:03 -06:00
Lance Edgar 9abbc001b3 Update changelog 2022-11-21 14:31:49 -06:00
Lance Edgar 4741ee0a7b Let the Luigi handler take care of removing some DB settings
so that command line can also remove them via same logic
2022-11-21 14:01:22 -06:00
Lance Edgar de5a8fae7c Update 'testing' watermark for dev background
for some reason Firefox suddenly would not display the old one.  so i
opened it in gimp, then re-exported to same filename.  apparently
something changed, this one worked in FF.

obviously not much care was taken in the migration here.  so maybe see
the previous file as starting point in case this needs revisiting
2022-11-20 21:01:15 -06:00
Lance Edgar a63d7e9b64 Update changelog 2022-11-20 20:26:48 -06:00
Lance Edgar 194f49c561 Add luigi module/class awareness for overnight tasks 2022-11-20 19:37:29 -06:00
Lance Edgar 922b550c17 Update changelog 2022-11-20 16:00:03 -06:00
Lance Edgar 7f0305fb7a Fix how keys are stored for luigi overnight/backfill tasks 2022-11-20 13:58:39 -06:00
Lance Edgar d4801f58e3 Make sure Grid class is included in package API docs 2022-11-19 21:45:23 -06:00
Lance Edgar e4392cd00a Allow disabling, or per-day scheduling, of problem reports 2022-11-19 17:44:09 -06:00
Lance Edgar 163c65600d Update changelog 2022-11-18 11:22:08 -06:00
Lance Edgar 3c740549e2 Turn on download results feature for Employees 2022-11-18 11:20:29 -06:00
Lance Edgar 3178894e4f Update changelog 2022-11-17 19:23:44 -06:00
Lance Edgar deed2111fb Add "between" verb for numeric grid filters 2022-11-15 16:29:15 -06:00
Lance Edgar 3e8924e7cc Update changelog 2022-11-15 13:39:17 -06:00
Lance Edgar fec259629e Let the auth handler manage user merge 2022-11-15 13:37:37 -06:00
Lance Edgar 3b64950a38 Update changelog 2022-11-03 11:34:32 -05:00
Lance Edgar be533922a2 Show UPC for receiving line item if no product reference
to help with troubleshooting invoice file parsing etc.
2022-11-03 11:28:38 -05:00
Lance Edgar 38e6441b61 Log a warning to troubleshoot luigi restart failure 2022-10-31 21:41:01 -05:00
Lance Edgar c2b2d11141 Use shared logic for rendering percentage values 2022-10-29 13:40:35 -05:00
Lance Edgar 22c33b58c7 Fix start_date param for pricing batch upload 2022-10-19 16:26:05 -05:00
Lance Edgar 9b101963e5 Use people handler to update address 2022-10-18 10:55:47 -05:00
Lance Edgar 620447f029 Add version workaround for sphinx-rtd-theme bug 2022-09-25 09:18:34 -05:00
Lance Edgar 733e7ee00c Add template method for rendering row grid component
so custom event hooks can be added more easily, when needed
2022-09-24 10:34:32 -05:00
Lance Edgar 3877346b3a Update changelog 2022-09-09 14:53:47 -05:00
Lance Edgar e67cde4255 Avoid use of self.handler within batch API views 2022-09-07 20:46:18 -05:00
Lance Edgar e46f4bf01e Do not convert date if already a date 2022-09-06 22:19:01 -05:00
Lance Edgar f7a019ed83 Make past item lookup optional for custorders 2022-09-06 16:44:26 -05:00
Lance Edgar 2950827c63 Add basic per-item discount support for custorders 2022-09-06 16:31:59 -05:00
Lance Edgar b37f63a231 Update changelog 2022-09-06 13:21:29 -05:00
Lance Edgar 365e4a4194 Convert value for more date filters; only add condition if valid
missed these in 187fea6d1b
2022-09-06 13:09:14 -05:00
Lance Edgar c43a4edec7 Move logic for "bulk-delete row objects" into MasterView
i guess so far it has only been needed for batch, but some day surely
it will be needed for something else..?

some of the template logic is still batch only i think..
2022-08-31 20:55:03 -05:00
Lance Edgar b5a519d132 Disable "Delete Results" button if no results, for row grid 2022-08-31 16:41:58 -05:00
Lance Edgar 35728e20be Add default normalize logic for API views
and use common logic for getting field list in traditional Form class
2022-08-30 21:56:46 -05:00
Lance Edgar 960d6279a9 Include WorkOrder.estimated_total for API 2022-08-30 21:14:01 -05:00
Lance Edgar 9ea103c0eb Update changelog 2022-08-30 14:18:57 -05:00
Lance Edgar 12e4b0a139 Expose more attrs for new product batch rows 2022-08-30 13:57:18 -05:00
Lance Edgar 731c2168b0 Improve parsing of purchase order quantities 2022-08-30 11:28:48 -05:00
Lance Edgar ef045607d9 Update changelog 2022-08-30 11:04:26 -05:00
Lance Edgar bb4e98af8d Add uom fields, configurable template for newproduct batch 2022-08-30 10:58:13 -05:00
Lance Edgar 6ea8a02b57 Add 'warning' flash messages to old jquery base template 2022-08-27 23:36:09 -05:00
Lance Edgar 187fea6d1b Convert value for date filter; only add condition if valid 2022-08-27 22:45:52 -05:00
Lance Edgar 36ba6f1463 Update changelog 2022-08-25 22:18:33 -05:00
Lance Edgar f005ef4d52 Add max lengths when editing person name via profile view 2022-08-25 22:15:56 -05:00
Lance Edgar 6a0a4627b4 Avoid error when no datasync profiles configured
at least, according to the web app none are configured..but they may
be in another config file
2022-08-24 20:06:38 -05:00
Lance Edgar 2dbba970b9 Only run tests if requested, for release task 2022-08-24 18:29:46 -05:00
Lance Edgar bcedc58d9f Update changelog 2022-08-24 18:24:42 -05:00
Lance Edgar 78500770d9 Add basic support for backfill Luigi tasks
idea being, sometimes you must import many days worth of data into
Trainwreck or what-not, and it must be split up b/c e.g. it would take
too long to import all at once (i.e. might interfere with overnight
tasks)
2022-08-23 23:27:47 -05:00
Lance Edgar 488696cb39 Fix index title for datasync configure page 2022-08-22 01:07:58 -05:00
Lance Edgar 6dfda20116 Update changelog 2022-08-21 20:41:55 -05:00
Lance Edgar e50356d276 Expose, honor "admin-ish" flag for roles
prevent user (un)assignment etc. unless admin is doing it
2022-08-21 19:36:48 -05:00
Lance Edgar 7b2fef5f09 Allow configuring datasync watcher kwargs 2022-08-21 15:22:29 -05:00
Lance Edgar 87cced1637 Fix perm check 2022-08-21 11:32:39 -05:00
Lance Edgar 2ce242ba42 Make textout scrolling "smooth" for upgrade progress 2022-08-20 23:33:46 -05:00
Lance Edgar bdbbe990dd Add global context from handler, for email previews 2022-08-20 23:07:19 -05:00
Lance Edgar 2ca93a07e9 Make separate tasks for collect vs. transmit of upgrade progress data 2022-08-20 22:40:16 -05:00
Lance Edgar 0a113611e8 Let just one "task" handle collect/transmit of progress for websocket
first client to connect, will cause task to start; subsequent clients
are just added to running set, for broadcast messaging
2022-08-20 21:19:20 -05:00
Lance Edgar e93063a344 Refactor upgrade websocket progress, so "anyone" can join in to see
now while an upgrade is executing, anyone with permission can "view"
the upgrade and see the same progress the executor is seeing
2022-08-20 18:56:35 -05:00
Lance Edgar 18cec49a86 Add websockets progress, "multi-system" support for upgrades
and related things to better support that
2022-08-20 17:39:33 -05:00
Lance Edgar db3f215ebe Add way to declare failure for an upgrade
doesn't really cancel it, since Tailbone isn't actually tracking the
subprocess etc.  but saves a step when something goes off the rails
2022-08-19 17:20:01 -05:00
Lance Edgar 8470126918 Add render_person_profile() method to MasterView 2022-08-18 19:22:04 -05:00
Lance Edgar 9566a882b5 Install dependencies when running tests etc. via tox 2022-08-18 18:23:30 -05:00
Lance Edgar 7d72a43ecd Use pytest instead of nosetests, for tox runs 2022-08-18 18:19:54 -05:00
Lance Edgar 8afc376636 Update changelog 2022-08-18 17:29:13 -05:00
Lance Edgar 89da6ae501 Expose setting for auto-correct when receiving from invoice 2022-08-18 17:27:30 -05:00
Lance Edgar d23e5d169a Add basic views for Luigi / overnight tasks 2022-08-18 15:11:09 -05:00
Lance Edgar 9de35a6e8b Add brief delay before declaring websocket broken 2022-08-17 22:59:50 -05:00
Lance Edgar d8de36b5ac Update changelog 2022-08-17 21:30:39 -05:00
Lance Edgar 2fde1db83c Allow user feedback to request email reply back 2022-08-17 21:08:54 -05:00
Lance Edgar 5fb99c54c9 Fix initial datasync status display when supervisor error occurs 2022-08-17 19:06:02 -05:00
Lance Edgar ed55fbca9e Log a warning if can't get supervisor process info 2022-08-17 18:44:10 -05:00
Lance Edgar 2375733d0f Add first experiment with websockets, for datasync status page 2022-08-17 18:24:55 -05:00
Lance Edgar 065f845707 Add proper status page for datasync
or rather, it's a good start..  plenty more could be added
2022-08-15 21:06:19 -05:00
Lance Edgar 839c4e0c28 Add get_next_url_after_submit_new_order() for customer orders
after new custorder batch is executed, where do we send user?
2022-08-14 17:33:12 -05:00
Lance Edgar a20eb468df Redirect to custom index URL when user cancels new custorder entry 2022-08-14 15:53:43 -05:00
Lance Edgar 303eba6bca Update changelog 2022-08-14 10:17:52 -05:00
Lance Edgar bc51a868ce Consolidate master API view logic
also let all API views use new config defaults convention
2022-08-14 00:59:35 -05:00
Lance Edgar f2c73acd3b Refactor usage of get_vendor() lookup 2022-08-13 23:59:09 -05:00
Lance Edgar 2f5de67ee7 Move handheld batch view module to appropriate location 2022-08-13 23:23:30 -05:00
Lance Edgar db3ea2e34a Fix default help URLs for ordering, receiving 2022-08-13 23:12:39 -05:00
Lance Edgar 2388ab88b6 Add the FormPosterMixin to ProfileInfo component 2022-08-12 20:47:32 -05:00
Lance Edgar e49a31df6a Avoid double-quotes in field error messages JS code 2022-08-12 19:47:25 -05:00
Lance Edgar d5a9aa6925 Update changelog 2022-08-12 18:29:46 -05:00
Lance Edgar 409a49ba20 Standardize merge logic when a handler is defined for it
also adds basic merge support for products view
2022-08-12 14:27:26 -05:00
Lance Edgar 4c29a667cb Couple of API tweaks for work orders
made a change to sorting such that it assumes the primary model is
being sorted, if caller does not specify
2022-08-11 00:15:12 -05:00
Lance Edgar 8d70107b5d Update changelog 2022-08-10 18:58:18 -05:00
Lance Edgar 51aeb50d39 Allow download results for Customers grid 2022-08-10 18:55:59 -05:00
Lance Edgar 0e8f383c14 Fix sequence of events re: grid component creation
somehow if the master view template had rows, the Delete Results
button was not working.  not clear when that problem started?! but
this seemed to be the correct fix
2022-08-09 23:26:41 -05:00
Lance Edgar ca5e2c1ff9 Add initial views for work orders
at least a head start maybe
2022-08-09 22:57:26 -05:00
Lance Edgar a6d5b262f9 Log traceback output when batch action subprocess fails 2022-08-09 16:35:48 -05:00
Lance Edgar 5952df82ff Tweak flash msg, logging when batch population fails 2022-08-09 15:05:03 -05:00
Lance Edgar 8f1f8abf42 Fix HTML literal for hidden form field 2022-08-09 14:48:23 -05:00
Lance Edgar 3edbe96968 Some API tweaks to support a byjove app 2022-08-09 14:37:41 -05:00
Lance Edgar d6aeb1d10f Add convenience wrapper to make customer field widget, etc.
customer widget is either autocomplete or dropdown, per config

also added a way to pass arbitrary kwargs to the chameleon template
rendering for a field

also moved the logic for rendering a <b-field> out of the template and
into the Form class

also start to prefer `input_handler` over `input_callback` when
specifying client-side JS hook
2022-08-09 14:37:29 -05:00
Lance Edgar 5334cf1871 Update changelog 2022-08-08 18:13:34 -05:00
Lance Edgar a999b996fb Add separate product grid filters for Category Code, Category Name
this also fixes a join bug in some edge cases
2022-08-08 14:39:26 -05:00
Lance Edgar 903afc111e Update changelog 2022-08-08 09:42:54 -05:00
Lance Edgar 3413d7c6f6 Expose setting for sendmail failure alerts 2022-08-07 18:45:45 -05:00
Lance Edgar fe4c3d4942 Make sure "configure" pages use AppHandler to save/delete settings
so that beaker config cache is invalidated, if in use
2022-08-07 18:23:15 -05:00
Lance Edgar 6352a6dc9a Add button to raise bogus error, for testing email alerts 2022-08-07 12:58:49 -05:00
Lance Edgar 172dbba8aa Update changelog 2022-08-07 10:10:17 -05:00
Lance Edgar 1152fba067 Always show "all" email settings if user has config perm
also tweak view config, per newer convention
2022-08-06 22:57:10 -05:00
Lance Edgar d74025318e Update changelog 2022-08-06 20:48:34 -05:00
Lance Edgar dd2631d27c Only show "all" emails if config says to use the entry points
otherwise traditional behavior needs to be preserved as the default,
for now...
2022-08-06 19:18:49 -05:00
Lance Edgar d52a186e12 Add support for toggling visibility of email profile settings 2022-08-06 18:38:17 -05:00
Lance Edgar 7d3f2e6bdf Update changelog 2022-08-05 13:28:47 -05:00
Lance Edgar 8776cd19dd Clean up URL routes for row CRUD 2022-08-05 12:09:32 -05:00
Lance Edgar 9c31e92c01 Update changelog 2022-08-04 09:08:56 -05:00
Lance Edgar cd9004b32b Invalidate config cache when raw setting is deleted 2022-08-04 08:14:04 -05:00
Lance Edgar ba8faacbd0 Update changelog 2022-08-03 16:58:06 -05:00
Lance Edgar 927470db72 Force cache invalidation when Raw Setting is edited
only applies if caching is actually in use
2022-08-03 15:15:49 -05:00
Lance Edgar 4ff0450632 Stop using the old rattail.db.api.settings module 2022-08-03 14:50:45 -05:00
Lance Edgar 862198cf82 Improve "touch" logic for employees
also use app handler for default touch logic
2022-08-03 11:13:43 -05:00
Lance Edgar 3726a2685a Update changelog 2022-07-27 10:21:08 -05:00
Lance Edgar 17810d9cae Misc. improvements for desktop receiving views
- don't expose "cases" if config says not to
- don't expose "expired" if config says not to
- use `numeric-input` for quantity fields
- add `product_key_field` to global-ish template context
2022-07-26 16:30:04 -05:00
Lance Edgar 92a52133de Add some more views to potentially include via poser 2022-07-26 14:25:20 -05:00
Lance Edgar 9589606fb5 Update changelog 2022-07-25 11:42:46 -05:00
Lance Edgar ad7b347e16 Add "auto-receive all items" support for receiving batch API 2022-07-24 22:29:55 -05:00
Lance Edgar f33d7b7f90 Add iter(Form) logic, to loop through fields 2022-07-24 21:11:36 -05:00
Lance Edgar 36d4f0a5f7 Add basic edit support for Purchases 2022-07-24 21:10:52 -05:00
Lance Edgar 0dc344b821 Assume default vendor for new receiving batch
i.e. if there is only one vendor
2022-07-24 15:05:51 -05:00
Lance Edgar 25f39f4173 Add basic/minimal merge support for customers 2022-07-24 13:21:47 -05:00
Lance Edgar e656f769b1 Allow optional row grid title for master view 2022-07-23 22:18:17 -05:00
Lance Edgar 28238c6fb5 Add setting to expose/hide "active in POS" customer flag 2022-07-23 22:09:47 -05:00
Lance Edgar e77ca93d80 Update changelog 2022-07-22 12:41:54 -05:00
Lance Edgar da3aaafbcd Misc deform template improvements
for sake of a custom form
2022-07-20 21:36:52 -05:00
Lance Edgar 10628eeb91 Add template_kwargs_clone() stub for master view 2022-07-20 11:01:22 -05:00
Lance Edgar 20aa6a3fbb Expose the complete flag for pricing batch
also update view config defaults per new convention
2022-07-19 16:36:21 -05:00
Lance Edgar e9edf205d9 Make caching products optional, when creating vendor catalog batch 2022-07-19 15:50:57 -05:00
Lance Edgar 6397a93f97 Allow download of results for common product-related tables 2022-07-19 14:52:31 -05:00
Lance Edgar 9c5f3a3b64 Split out rendering of this-page component in falafel theme
it's possible a template may need to override that
2022-07-19 11:45:52 -05:00
Lance Edgar 5e0253927c Update changelog 2022-07-18 12:41:27 -05:00
Lance Edgar d16290cb70 Add new-style config defaults for BrandView 2022-07-18 12:31:54 -05:00
Lance Edgar c6df827311 Add basic "download results" for Subdepartments grid 2022-07-08 12:57:57 -05:00
Lance Edgar 496e03a3ec Honor default pagesize for all grids, per setting 2022-07-01 12:00:17 -05:00
Lance Edgar 7e0e881017 Fix form validation for app settings page w/ buefy theme 2022-07-01 12:00:06 -05:00
Lance Edgar 11cda10ca5 Update changelog 2022-06-24 14:20:17 -05:00
Lance Edgar a289216eac Add autocomplete support for subdepartments 2022-06-14 17:52:59 -05:00
Lance Edgar c79ecab719 Add minimal buefy support for 'percentinput' field widget
this isn't complete but seems to work well enough so far..
2022-06-14 17:40:06 -05:00
Lance Edgar 4fb226ad98 Update changelog 2022-06-14 14:03:42 -05:00
Lance Edgar a28d9b9748 Use build module instead of invoking setup.py for release 2022-06-14 14:03:10 -05:00
Lance Edgar 6b466bb90f Add start date support for "future" pricing batch 2022-06-14 13:51:00 -05:00
Lance Edgar cb6499522e Let default grid page size correspond to first option 2022-06-14 11:25:29 -05:00
Lance Edgar 78a9ba5084 Update changelog 2022-05-15 16:47:31 -05:00
Lance Edgar cff4942769 Allow restricting to supported vendors only, for Receiving 2022-05-15 16:45:31 -05:00
Lance Edgar e3b1be5835 Expose config for identifying supported vendors
unfortunately must identify vendors at each app node separately, but
this is definitely still an improvement..
2022-05-15 16:04:22 -05:00
Lance Edgar 983a06abe3 Update changelog 2022-05-10 20:08:06 -05:00
Lance Edgar 75319c0d6a Add grid workarounds when data is list instead of query
ugh, this is not very intuitive.  pretty sure all that needs an
overhaul someday
2022-05-10 20:06:21 -05:00
Lance Edgar 18c3c57930 Sort roles list when viewing a user 2022-05-03 14:13:47 -05:00
Lance Edgar c371db3534 Update changelog 2022-05-03 13:43:57 -05:00
Lance Edgar a49aa77ec0 Tweak how family data is displayed 2022-05-03 13:36:14 -05:00
Lance Edgar 129455a31f Update changelog 2022-04-13 20:19:00 -05:00
Lance Edgar 10a801aa10 Flush early when populating batch, to ensure error is shown 2022-04-13 16:42:47 -05:00
Lance Edgar aea7f01047 Fix quotes for field helptext 2022-04-07 12:57:40 -05:00
Lance Edgar 56c5c4e540 Update changelog 2022-04-04 13:57:31 -05:00
Lance Edgar d48a92c88d Fix "touch" url for non-standard record types 2022-04-04 13:56:27 -05:00
Lance Edgar aa37fc3add Tweak where description field is shown for receiving batch 2022-04-03 14:42:40 -05:00
Lance Edgar 1bb41b21af Honor case vs. unit restrictions for new custorder
and expose them in config view
2022-03-29 18:19:14 -05:00
Lance Edgar 4e25e87bfb Log error when failing to submit new custorder batch 2022-03-29 17:43:42 -05:00
Lance Edgar 80b9593651 Add template kwargs stub for view_row() 2022-03-29 17:30:37 -05:00
Lance Edgar edef084121 Raise 404 if report not found 2022-03-29 17:19:23 -05:00
Lance Edgar fc32542f55 Add touch for report codes 2022-03-29 17:19:14 -05:00
Lance Edgar efcfd787af Update changelog 2022-03-29 11:49:24 -05:00
Lance Edgar 700b5f0b91 Let errors raise when showing poser reports 2022-03-29 11:39:32 -05:00
Lance Edgar dfc88193b2 Update changelog 2022-03-29 10:32:03 -05:00
Lance Edgar b4d5d70e4c Force session flush within try/catch, for batch refresh 2022-03-26 15:29:28 -05:00
Lance Edgar 1bad1cd3e7 Update changelog 2022-03-25 22:18:01 -05:00
Lance Edgar f0b6b62791 Use common logic for fetching batch handler 2022-03-25 13:49:39 -05:00
Lance Edgar ae1e9dba0f Improve vendor validation for new receiving batch 2022-03-25 12:33:37 -05:00
Lance Edgar 777a7afd46 Update changelog 2022-03-21 17:33:26 -05:00
Lance Edgar ab3a66542d Show link to txn as field when viewing trainwreck item 2022-03-17 21:19:05 -05:00
Lance Edgar c72d99794e Update changelog 2022-03-17 17:35:00 -05:00
Lance Edgar fc5b931007 Expose custorder xref markers for trainwreck 2022-03-17 16:59:50 -05:00
Lance Edgar cdae4bf8da Update changelog 2022-03-16 21:28:47 -05:00
Lance Edgar 71d8d5a70d Make problem report titles searchable in grid
at least if buefy version is new enough
2022-03-16 21:27:59 -05:00
Lance Edgar 322335f4ab Show helptext when applicable for "new batch from product query" 2022-03-15 22:58:19 -05:00
Lance Edgar 0904cda2c6 Always show batch params by default when viewing 2022-03-15 22:53:24 -05:00
Lance Edgar fad8b44be2 Update changelog 2022-03-15 19:55:55 -05:00
Lance Edgar da910b1414 Add default help link for Receiving feature
also stop showing "buyer" filter by default
2022-03-11 20:55:01 -06:00
Lance Edgar 6037519fbe Log error instead of warning, when batch population fails
user experience does not change but should help the admin to track
down the problem quicker..
2022-03-11 12:37:43 -06:00
Lance Edgar 7e15f75d44 Update changelog 2022-03-10 10:19:55 -06:00
Lance Edgar 25ecade1e6 Add "batch" to model title for new customer order batch
just to make things a bit more clear..
2022-03-10 10:18:43 -06:00
Lance Edgar 69161b7037 Default behavior for report chooser should *not* be form/dropdown 2022-03-10 09:55:42 -06:00
Lance Edgar 4e892d09ec Add line break for report chooser page 2022-03-10 09:53:15 -06:00
Lance Edgar e284370c4b Add Form.insert() method, to insert field based on index 2022-03-09 19:41:46 -06:00
Lance Edgar 01b78d7513 Add workaround when inserting new fields to form field list
i.e. if inserting "before" or "after" a field which does not exist
2022-03-09 18:39:12 -06:00
Lance Edgar b9fa324bb4 Cleanup view config syntax for vendor catalog batch
also make sure vendor autocomplete url exists, before using that
widget.  this can be an issue when app deals "directly" with POS when
making the batch etc.
2022-03-09 18:26:27 -06:00
Lance Edgar 0a42ec77b2 Cleanup grid filters for vendor catalog batches 2022-03-08 16:35:11 -06:00
Lance Edgar a9e64e931e Update changelog 2022-03-08 14:49:00 -06:00
Lance Edgar caa13f5a75 Bump the default Buefy version to 0.8.13
0.8.6 seemed to be causing some problems.  probably need to bump it
even further but 0.8.13 has been the "soft default" for a while..
2022-03-08 14:47:00 -06:00
Lance Edgar 9d5adf7793 Fix gotcha when defining new provider views
UI should show the key if label is missing
2022-03-07 17:40:48 -06:00
Lance Edgar 8f4b223125 Log warning/traceback when failing to include a configured view 2022-03-07 17:12:06 -06:00
Lance Edgar e38cfda076 Update changelog 2022-03-07 11:16:25 -06:00
Lance Edgar 511e185f33 Link to email settings profile when viewing email attempt 2022-03-07 10:53:12 -06:00
Lance Edgar 7c4e9b56c7 Let tailbone providers include static views
also add more native (batch) views to default list
2022-03-06 22:06:57 -06:00
Lance Edgar d18bade951 Let providers add extra views, options for includes config 2022-03-06 19:03:08 -06:00
Lance Edgar c4e872c94c Add the "provider" concept, let them configure db sessions
more to come...
2022-03-06 18:49:09 -06:00
Lance Edgar 57f3b942e5 Update changelog 2022-03-05 14:53:09 -06:00
Lance Edgar 37d4ef751c Add flash message when upgrade execution completes (pass or fail) 2022-03-05 14:31:43 -06:00
Lance Edgar b5effaa01b Add tailbone.views.essentials to include common / "core" views 2022-03-05 10:50:33 -06:00
Lance Edgar 66a15fb9a1 Add initial/basic support for configuring "included views"
also stub for managing "poser views"
2022-03-05 09:26:25 -06:00
Lance Edgar 33abeb1aca Improve the Poser Setup page; allow poser dir refresh 2022-03-05 09:12:01 -06:00
Lance Edgar 128657810b Add PoserMasterView, rename route for poser_reports
must use e.g. `poser_reports` and `poser_views` for the "meta" stuff,
i.e. maintenance of actual poser things, b/c it will be possible to
define poser views, and those routes should be `poser.*` probably..
2022-03-05 09:10:05 -06:00
Lance Edgar f5d24133f7 Make common web view a bit more common
i.e. avoid the need to subclass it in derived projects
2022-03-04 17:44:34 -06:00
Lance Edgar a28a801a62 Update some more view config syntax
some common ones used by a particular app..
2022-03-04 12:32:28 -06:00
Lance Edgar 738d5d94e0 Always include app_title in global template rendering context 2022-03-03 19:14:21 -06:00
Lance Edgar 3fae9e6270 Show link back to Poser Report when viewing Generated Report
i.e. where applicable / possible.  also allow bulk-delete of generated
reports, and show name filter by default for that grid
2022-03-03 18:47:26 -06:00
Lance Edgar 691a5e84f9 Show list of generated reports when viewing Poser Report 2022-03-03 18:36:35 -06:00
Lance Edgar 18625efa87 Update changelog 2022-03-02 21:33:21 -06:00
Lance Edgar d99f2541df Add dedicated perm for replacing poser report module 2022-03-02 18:52:28 -06:00
Lance Edgar 72177aef0a Add basic support for Poser reports, list/create 2022-03-02 17:21:38 -06:00
Lance Edgar a3195267c9 Show toast msg instead of alert after sending feedback 2022-03-01 19:51:30 -06:00
Lance Edgar 8104657ae9 Update changelog 2022-03-01 16:14:18 -06:00
Lance Edgar 78fb38e072 Tweak styles for links in object helper panel 2022-03-01 15:18:47 -06:00
Lance Edgar 206d51f59b Params should be readonly when editing batch 2022-03-01 15:03:48 -06:00
Lance Edgar 2e0bc63e20 Update changelog 2022-03-01 13:31:50 -06:00
Lance Edgar 031d97aea3 Avoid making discounts data if missing field, for trainwreck item view 2022-03-01 13:01:59 -06:00
Lance Edgar 59a9d2cf86 Pass query along for download results, so subclass can modify 2022-03-01 12:17:06 -06:00
Lance Edgar ee961edf94 Fix stdout/stderr fields for upgrade view
whoops..missed that one
2022-02-28 22:16:52 -06:00
Lance Edgar 7b485d5ad2 Remove some duplicated code
in fact it wasn't exactly duplicate..it had a bug which the shared
function code does not have
2022-02-28 12:05:16 -06:00
Lance Edgar ec2600ddf7 Add simple searchable column support for non-AJAX grids
idk maybe even AJAX grids can use?  not gonna try at the moment
2022-02-26 21:00:05 -06:00
Lance Edgar 63fef16c37 Update changelog 2022-02-26 20:09:30 -06:00
Lance Edgar 74fecf553e Add page/way to configure main menus
just the basics so far, index page routes and separators should be
supported but nothing else.  also "menus from config" is all or
nothing, no way to mix config + code at this point
2022-02-26 17:22:54 -06:00
Lance Edgar 587a4daf7a Update changelog 2022-02-25 14:30:02 -06:00
Lance Edgar 2290d9f990 Expose "discount type" for Trainwrewck line items 2022-02-24 10:39:11 -06:00
Lance Edgar 3553f23eab Use dict instead of custom object to represent menus
as prep for editing menu config directly in app
2022-02-23 00:26:14 -06:00
Lance Edgar 0c5992ad75 Add grid hyperlinks for trainwreck transaction line items 2022-02-22 20:39:06 -06:00
Lance Edgar 8ae1b87a1e Auto-filter hyperlinks for PO vs. invoice breakdown in Receiving 2022-02-20 19:52:24 -06:00
Lance Edgar 4d404cb20b Add auto-filter hyperlinks for batch row status breakdown 2022-02-20 19:40:32 -06:00
Lance Edgar 5b697cdf26 Add view template stub for trainwreck transaction 2022-02-20 17:06:51 -06:00
Lance Edgar ceceb3f030 Update changelog 2022-02-20 15:25:17 -06:00
Lance Edgar 66fd2ff5e6 Show SRP as currency for vendor catalog batch 2022-02-19 21:00:54 -06:00
Lance Edgar 7f06b3e53b Expose per-item discounts for Trainwreck 2022-02-19 17:31:14 -06:00
Lance Edgar e990be3570 Expose some new trainwreck fields 2022-02-19 14:39:40 -06:00
Lance Edgar 57e22c9ff5 Only show DB picker for permissioned users 2022-02-18 15:39:12 -06:00
Lance Edgar b6bd095d8e Update changelog 2022-02-16 16:33:49 -06:00
Lance Edgar 778578292f Fix progress bar when running problem report 2022-02-16 16:16:40 -06:00
Lance Edgar 8744ee74b3 Update changelog 2022-02-15 17:34:28 -06:00
Lance Edgar 47bfcc23cb Add FormPosterMixin to WholePage class 2022-02-15 10:15:08 -06:00
Lance Edgar 962d31c4c2 Add initial support for editing user preferences
by default this exposes just one setting which has only one possible
value, so not very useful.  but can override as needed
2022-02-14 20:30:15 -06:00
Lance Edgar 6093be43c9 Allow override of navbar-end element in falafel theme header 2022-02-13 21:51:42 -06:00
Lance Edgar 753daa55e8 Update changelog 2022-02-13 21:41:47 -06:00
Lance Edgar 09227fa30a New upgrades should be enabled by default 2022-02-13 16:27:24 -06:00
Lance Edgar 4e3aa1af83 Tweak how "duration" fields are rendered for grids, forms 2022-02-12 19:16:16 -06:00
Lance Edgar a6d97538af Use new-style config defaults for customer views 2022-02-11 19:15:39 -06:00
Lance Edgar 85ef73dcb9 Tell browser not to cache certain pages, by default
main grid/index pages, and any view page which contains a row grid
2022-02-11 16:55:25 -06:00
Lance Edgar 0ead06106c Add config for showing ordered vs. shipped amounts when receiving 2022-02-11 16:48:46 -06:00
Lance Edgar 86a42064ea Cleanup labels for Vendor/Code "preferred" vs. "any" in products grid 2022-02-11 15:35:12 -06:00
Lance Edgar 9584fb57b0 Only prevent cache for index pages if so configured
there is a performance hit for this, depending on your perspective, so
let's make it opt-in only for now
2022-02-10 20:31:03 -06:00
Lance Edgar e852613567 Add highlight for non-active customers in grid 2022-02-10 11:16:39 -06:00
Lance Edgar 065ad9e422 Add highlight for non-active users in grid 2022-02-10 10:55:41 -06:00
Lance Edgar f1c2fd399e Try out new config defaults function for user views
pretty sure this is a good idea but we'll see
2022-02-09 18:02:09 -06:00
Lance Edgar 8cc54b6106 Update changelog 2022-02-08 12:23:12 -06:00
Lance Edgar 072f5da69d Add "full lookup" product search modal for new custorder page 2022-02-08 12:21:24 -06:00
Lance Edgar 025cabd1ad Update changelog 2022-02-05 21:52:30 -06:00
Lance Edgar b261e8bb9b Add some autocomplete workarounds for new vendor catalog batch
when user selects a parser, it may auto-select the vendor, but keeping
that all in sync is complicated.  this seems to be an improvement but
it could likely use more..
2022-02-05 21:41:05 -06:00
Lance Edgar a36f775752 Tweak how product key field is handled for product views 2022-02-05 15:59:36 -06:00
Lance Edgar 091b479a02 Update changelog 2022-02-04 14:56:18 -06:00
Lance Edgar 9c75d7b560 Add CustomerGroupAssignment to customer version history 2022-02-04 14:42:11 -06:00
Lance Edgar ceb70eec4c Update changelog 2022-02-01 20:03:09 -06:00
Lance Edgar ea180ca107 Expose batch params for vendor catalogs 2022-02-01 19:14:16 -06:00
Lance Edgar 1117893900 Update changelog 2022-01-31 21:18:01 -06:00
Lance Edgar b22e7fd077 Make "generate report" the same as "create new generated report"
no reason to reinvent that wheel
2022-01-31 19:34:24 -06:00
Lance Edgar d677cb1bc8 Update changelog 2022-01-31 17:53:37 -06:00
Lance Edgar 15fc82fc34 Tweak handling of empty params when generating report
not sure there was a compelling reason to use `colander.null` other
than that is what pyramid generally does?  but `None` seems to work
fine for me so far..  (used w/ optional date param)
2022-01-31 17:51:03 -06:00
Lance Edgar 4716545b7e Show helptext for params when generating new report 2022-01-31 16:52:16 -06:00
Lance Edgar 16a4fe1a4f Update changelog 2022-01-31 14:52:55 -06:00
Lance Edgar 8a08b3f7c7 Add support for tailbone-integration project generator 2022-01-29 14:42:52 -06:00
Lance Edgar 999bb29499 Add support for rattail-integration project generator 2022-01-29 12:37:05 -06:00
Lance Edgar 1575cad447 Improve profile link helper for buefy themes 2022-01-29 08:57:03 -06:00
Lance Edgar cdcb106f2d Update changelog 2022-01-26 13:14:25 -06:00
Lance Edgar af14216eea Tweak the "auto-receive all" tool for Chrome browser
also split out each helper section
2022-01-26 13:13:00 -06:00
Lance Edgar b9c5f6a869 Update changelog 2022-01-25 11:10:23 -06:00
Lance Edgar 7e071a7daf Hopefully fix tox tests for python 2.7 2022-01-24 20:06:02 -06:00
Lance Edgar db3cd4ec6e Only expose "product" departments within product view dropdowns 2022-01-24 15:32:24 -06:00
Lance Edgar ae27c110ab Update changelog 2022-01-19 12:19:15 -06:00
Lance Edgar f83fc18ebc Use buefy input for quickie search
not sure why this suddenly has poor style / formatting, but this fixes
2022-01-18 12:29:23 -06:00
Lance Edgar 23fb5e09d1 Update changelog 2022-01-15 12:47:08 -06:00
Lance Edgar fe7612c885 Use the new label handler
also, move "print one-off labels" logic into product master view
2022-01-13 21:25:17 -06:00
Lance Edgar 517dd4ad9e Update changelog 2022-01-13 14:36:04 -06:00
Lance Edgar e672e9670f Strip whitespace for new customer fields, in new custorder page 2022-01-13 14:21:40 -06:00
Lance Edgar 0b25469f33 Update changelog 2022-01-12 18:24:27 -06:00
Lance Edgar 765b7b4957 Update usage of app.get_email_handler() to avoid warnings 2022-01-12 18:20:25 -06:00
Lance Edgar 9eeb921915 Include all static files in manifest 2022-01-12 18:20:01 -06:00
Lance Edgar 9045505153 Update changelog 2022-01-10 16:34:06 -06:00
Lance Edgar eb221417e5 Expose the Sale, TPR, Current price fields for label batch
still need to figure out how execution can print e.g. TPR prices...
2022-01-10 14:54:49 -06:00
Lance Edgar cabe422508 Add progress support when deleting a batch
b/c we must delete all rows individually, and some batches can be
several thousand rows each
2022-01-09 19:25:18 -06:00
Lance Edgar 8579b89002 Add way to set form-wide schema validator
was needed to enforce rule where one field is required only in some
cases, depending on value of another field
2022-01-09 18:13:12 -06:00
Lance Edgar 0545099a2b Add buefy support for quick-printing product labels; also speed bump 2022-01-09 15:20:35 -06:00
Lance Edgar 94fc5c1859 Update changelog 2022-01-08 20:08:32 -06:00
Lance Edgar 6af5157b4e Update some method calls to avoid deprecation warnings 2022-01-08 19:48:14 -06:00
Lance Edgar dc28b1337d Add config for supported vendor catalog parsers
also explicitly set "native value" for all configuration checkbox
fields, since apparently it will send `'false'` by default...
2022-01-08 13:35:59 -06:00
Lance Edgar 2ce7c93aeb Expose, honor "allow future" setting for vendor catalog batch 2022-01-08 12:19:35 -06:00
Lance Edgar 88b3279e63 Several disparate changes needed for vendor catalog improvements
- invoke vendor handler where appropriate, e.g. for parsers
- reverse "polarity" of dropdown chooser setting; rename it
- tweak autocomplete behavior yet again, for dynamic values
- auto-select vendor upon parser selection, when possible
2022-01-07 19:27:10 -06:00
Lance Edgar ab61778d35 Some aesthetic improvements for vendor catalog batch
hopefully they're improvements...
2022-01-07 15:03:56 -06:00
Lance Edgar f89dc88c0e Add configurable template file for vendor catalog batch 2022-01-06 12:53:01 -06:00
Lance Edgar ad110c2ce2 Remove unused import 2022-01-03 21:10:34 -06:00
Lance Edgar 5e0ba81b21 Update changelog 2022-01-03 16:17:00 -06:00
Lance Edgar a0bb481a43 Use AuthHandler.get_permissions()
instead of deprecated `cache_permissions()`
2022-01-03 15:34:00 -06:00
Lance Edgar 3aac855fa1 Add basic configure page for Trainwreck
also the beginnings of a "yearly rollover" page which hopefully will
prove useful for helping to automate that, once i figure out how best
to go about it...
2022-01-01 19:12:46 -06:00
Lance Edgar 94883c1433 Remove usage of app.get_designated_import_handler() 2021-12-31 13:46:36 -06:00
Lance Edgar 7b7eee92cd Fix permission check for input file template links 2021-12-30 11:04:59 -06:00
Lance Edgar c2d76966a3 Update changelog 2021-12-29 11:14:40 -06:00
Lance Edgar 82dfce6f81 Add basic "resolve" support for person, product from new custorder 2021-12-23 20:24:43 -06:00
Lance Edgar 1b0d6581db Bugfix 2021-12-23 16:18:06 -06:00
Lance Edgar 819ae22b0e Expose products setting for type 2 UPC lookup
also expose Configure button for most master view pages
2021-12-23 15:18:30 -06:00
Lance Edgar 33af2e6fa1 Show create button on "most" pages for a master view 2021-12-23 14:46:28 -06:00
Lance Edgar 4396c4c628 Update changelog 2021-12-23 12:42:12 -06:00
Lance Edgar daa5126c21 Improve email bounce view per buefy theme 2021-12-23 12:34:57 -06:00
Lance Edgar 494b1384c4 Bugfix 2021-12-22 16:45:56 -06:00
Lance Edgar c0db03bc28 Add basic "pending product" support for new custorder batch 2021-12-22 16:34:17 -06:00
Lance Edgar 408bffb775 Update changelog 2021-12-20 14:58:01 -06:00
Lance Edgar a6f608e8cc Flag discontinued items for main Products grid
no styling is applied but custom app can do so
2021-12-20 14:56:25 -06:00
Lance Edgar e97b8a9f7e Update changelog 2021-12-20 14:27:47 -06:00
Lance Edgar 31dff0d353 Add some standard CRUD buttons for buefy themes
finally!

also disable the permalink "feature" since it seems not useful
2021-12-17 22:08:37 -06:00
Lance Edgar 30f95e2f08 Add common configuration logic for "input file templates"
just used in one batch so far but should be useful for many more..once
can get around to migrating them

had to rework the configuration logic to use HTML form instead of
JSON, to allow for the file uploads
2021-12-17 20:25:17 -06:00
Lance Edgar 099b6915f4 Update changelog 2021-12-17 09:17:35 -06:00
Lance Edgar da6c782ac3 Fix how fallback/default buefy and vue.js versions are used 2021-12-16 20:05:56 -06:00
Lance Edgar e99c001673 Let config decide which versions of vue.js and buefy to use 2021-12-16 14:58:25 -06:00
Lance Edgar c7d587b4cb Tweak wording on base configure template 2021-12-16 14:16:21 -06:00
Lance Edgar ff348a2aa0 Add some minimal docs for Diff constructor 2021-12-15 18:07:22 -06:00
Lance Edgar bc7ccb6a9f Render "pretty" UPC by default, for batch row form fields 2021-12-15 18:06:53 -06:00
Lance Edgar 40d36f9808 Update changelog 2021-12-15 15:19:36 -06:00
Lance Edgar f49fdebd98 Add some smarts when making batch execution form schema
in some cases `has_execution_options()` may return True but the base
view class may not need to provide any options itself (i.e. subclass
is responsible for declaring the view has options).
2021-12-15 15:02:28 -06:00
Lance Edgar ca57bd3572 Auto-register all config pages, for dropdown in App Settings 2021-12-15 00:00:46 -06:00
Lance Edgar 6f62f141d2 Fix params field when deleting a report 2021-12-14 19:08:32 -06:00
Lance Edgar 197d3de74a Add "jump to" chooser in App Settings, for various "configure" pages 2021-12-13 22:32:10 -06:00
Lance Edgar 1244659064 Add more basic config views, obviating some App Settings 2021-12-13 21:33:10 -06:00
Lance Edgar 16bc3076ad Add basic config page for Products 2021-12-13 21:06:47 -06:00
Lance Edgar 1fbe429a08 Add basic "config" view for Receiving 2021-12-13 20:35:23 -06:00
Lance Edgar 340a177a29 Overhaul desktop views for receiving, for efficiency
still could use even more i'm sure, but this takes advantage of buefy
to add dialogs etc. from the "view receiving batch row" page.  this
batch no longer allows direct edit of rows but that's hopefully for
the better.
2021-12-13 18:37:47 -06:00
Lance Edgar 2f676774e9 Bugfix 2021-12-11 15:40:46 -06:00
Lance Edgar a2032a7be2 Allow for null price when showing price history 2021-12-10 16:33:53 -06:00
Lance Edgar f549858c5d Update changelog 2021-12-09 12:13:59 -06:00
Lance Edgar 9d02180c92 Add buttons to edit, confirm cost for receiving batch row view
not yet fully implemented
2021-12-08 22:31:54 -06:00
Lance Edgar e906c01e64 Make "view row" prettier for receiving batch, for buefy themes
this seems like a good direction; should make "receive product" and
"declare item" use b-modal on same page probably
2021-12-08 21:59:41 -06:00
Lance Edgar be92075abb Allow "auto-receive all items" batch feature in production
but require a dedicated permission
2021-12-08 20:26:31 -06:00
Lance Edgar 10e34b83ed Refactor "receive row" and "declare credit" tools per buefy theme 2021-12-08 19:44:50 -06:00
Lance Edgar ae76ceea04 Update changelog 2021-12-08 15:54:23 -06:00
Lance Edgar 6f60387f30 Fix bug when report has no params dict 2021-12-08 12:21:23 -06:00
Lance Edgar 60222c4977 Assume default receiving workflow if there is only one 2021-12-07 19:58:11 -06:00
Lance Edgar ff588b6a5c Only include --runas arg if we have a value 2021-12-07 19:57:26 -06:00
Lance Edgar 871dd35a3a Add basic views to expose Problem Reports, and run them
not very sophisticated yet but heck better than we had yesterday
2021-12-07 18:01:07 -06:00
Lance Edgar f687078bbf Update changelog 2021-12-07 16:19:32 -06:00
Lance Edgar 6fc666e221 Fix form ref bug, for batch execution 2021-12-07 16:18:56 -06:00
Lance Edgar 095afcde24 Update changelog 2021-12-07 13:19:18 -06:00
Lance Edgar 1353f6ed3c Bugfix 2021-12-07 12:09:43 -06:00
Lance Edgar a7c6380a3a Update changelog 2021-12-07 11:36:46 -06:00
Lance Edgar 5a4abbb163 When viewing report output, show params as proper buefy table
plus couple of other random tweaks
2021-12-07 11:28:23 -06:00
Lance Edgar 092f1cda0c Honor "safe for web app" flags for import/export handlers 2021-12-06 21:29:33 -06:00
Lance Edgar cc4b2278e7 OMG a ridiculous commit to overhaul import handler config etc.
- add `MasterView.configurable` concept, `/configure.mako` template
- add new master view for DataSync Threads (needs content)
- tweak view config for DataSync Changes accordingly
- update the Configure DataSync page per `configurable` concept
- add new Configure Import/Export page, per `configurable`
- add basic views for Raw Permissions
2021-12-06 20:04:34 -06:00
Lance Edgar 282185c5af Add basic import/export handler views, tool to run jobs 2021-12-05 17:23:11 -06:00
Lance Edgar 95da490f9a Update changelog 2021-12-03 09:44:20 -06:00
Lance Edgar 760fbc57bc Expose the Sale Price and TPR Price for product views
in addition to Current Price
2021-12-02 14:40:51 -06:00
Lance Edgar 47f6c941ec Update changelog 2021-11-29 21:03:20 -06:00
Lance Edgar 4229798c7b Add button to remove all datasync settings from DB
seems useful for someone testing, as prep to make the switch
2021-11-29 19:28:07 -06:00
Lance Edgar 8aff5d519d Add page for configuring datasync
experimental! until proven worthy..
2021-11-29 17:23:01 -06:00
Lance Edgar bb0666b77d Update changelog 2021-11-28 10:59:55 -06:00
Lance Edgar dbd00291b3 Add simple search filters for past items dialog in new custorder 2021-11-27 19:47:02 -06:00
Lance Edgar c1f9190613 Show current/sale pricing for products in new custorder page 2021-11-27 19:08:15 -06:00
Lance Edgar ce354d5bc3 Update changelog 2021-11-25 19:01:35 -06:00
Lance Edgar b9037111a4 Don't use multi-select for new report in buefy themes
also let app handler fetch the report handler
2021-11-25 18:56:28 -06:00
Lance Edgar 03dad82663 Add basic support for receiving from PO with invoice 2021-11-25 16:50:13 -06:00
Lance Edgar e8828efae3 Update changelog 2021-11-17 15:12:54 -06:00
Lance Edgar b8f1b7bd84 Show ordered quantity when viewing costing batch row 2021-11-17 14:57:10 -06:00
Lance Edgar 0fa888efaf Fix bug when product has empty suggested price 2021-11-16 17:23:56 -06:00
Lance Edgar f385aab44a Update changelog 2021-11-14 13:27:13 -06:00
Lance Edgar a7b91b5b31 Expose the "sync users" flag for Roles 2021-11-13 15:05:45 -06:00
Lance Edgar 901dacf038 Update changelog 2021-11-11 18:38:44 -06:00
Lance Edgar 426ba0ea34 Fix "download results" support for Products
it is not enabled by default, but still should work when it is
2021-11-11 17:42:59 -06:00
Lance Edgar 3a10a4bcb7 Improve error handling when executing a custorder batch 2021-11-11 13:37:10 -06:00
Lance Edgar 6e15d59a84 Update changelog 2021-11-11 12:31:42 -06:00
Lance Edgar f1fd003dca Add permission for viewing "all" employees
previously we showed all if user had "edit" perm
2021-11-11 12:30:00 -06:00
Lance Edgar 1ceb1e4434 Update changelog 2021-11-11 12:11:24 -06:00
Lance Edgar 5f9d311cdb Add views for PendingProduct model; also DepartmentWidget 2021-11-10 12:39:51 -06:00
Lance Edgar 7630f504b0 Add initial VersionMasterView
for those times when you just need to expose a version table directly
2021-11-09 17:20:53 -06:00
Lance Edgar e7871380a9 Add "true margin" to products XLSX export 2021-11-09 15:49:42 -06:00
Lance Edgar 85166d5beb Update changelog 2021-11-09 11:51:21 -06:00
Lance Edgar 90cc8e5370 Fix dynamic content title for "view profile" page 2021-11-08 20:17:07 -06:00
Lance Edgar a12318246f Update changelog 2021-11-08 18:33:19 -06:00
Lance Edgar eb28fc2e3c Fall back to empty string for product regular price
i think this avoids a bug when a product has no regular price but does
have a current price
2021-11-08 13:15:10 -06:00
Lance Edgar fec7c3b3ee Cleanup grid columns for receiving batches 2021-11-07 18:10:28 -06:00
Lance Edgar 23d38604c4 Let handler restrict available invoice parser options 2021-11-07 17:10:33 -06:00
Lance Edgar 67c1adcc75 Tweak how we fetch invoice parser
per changes in rattail
2021-11-07 14:12:06 -06:00
Lance Edgar 3990854d42 Fix product URL for a new custorder scenario 2021-11-06 20:31:55 -05:00
Lance Edgar 5d875bc731 Let user "add past product" when making new custorder 2021-11-06 20:00:54 -05:00
Lance Edgar ddb05afe6b Auto-select Quantity tab when editing item for new custorder
also be a little smarter on error when user selects an item
2021-11-06 17:56:35 -05:00
Lance Edgar 43bbc2a29e Show some more product attributes in custorder item selection popup 2021-11-06 17:37:05 -05:00
Lance Edgar 7a5ba0503a Use products handler to get image URL 2021-11-06 17:36:19 -05:00
Lance Edgar 28e9085249 Update changelog 2021-11-05 18:45:45 -05:00
Lance Edgar 5ff57ae7d2 Add link to download generic template for vendor catalog batch
also let config restrict which parsers are "supported"

and auto-choose parser if there is only one
2021-11-05 18:40:46 -05:00
Lance Edgar df8778f85d Add render_brand() method for MasterView 2021-11-05 15:11:30 -05:00
Lance Edgar 2be1d12116 Make separate method for writing results XLSX file
so subclass can customize
2021-11-05 15:11:07 -05:00
Lance Edgar eb76d868ca Update changelog 2021-11-04 21:25:32 -05:00
Lance Edgar b34d88d704 Avoid exposing batch params when creating a batch
not sure how this never came up until now..?
2021-11-04 21:20:42 -05:00
Lance Edgar 0758ca09e6 Show unit price in line items grid for new custorder
maybe should change this to show "base price" (unit *or* case
depending on the row uom) ?
2021-11-03 20:54:46 -05:00
Lance Edgar 1bdb845032 Honor the "product price may be questionable" flag for new custorder
i.e. don't expose the per-item flag unless *that* flag is set
2021-11-03 20:20:22 -05:00
Lance Edgar 4d33e3dcbe Move some custorder logic to handler; allow force-swap of product selection 2021-11-03 19:19:20 -05:00
Lance Edgar b0fa559760 Fix product view page when user cannot view version history 2021-11-03 18:30:16 -05:00
Lance Edgar 8a378317c0 Try to prevent caching for any /index (grid) page
if this works, maybe also should do it for /view since that can have a
rows grid?
2021-11-03 18:15:13 -05:00
Lance Edgar a6b7056f2a Update changelog 2021-11-03 16:49:04 -05:00
Lance Edgar 9fef4c2601 Fix the Department filter for Products grid, for jquery themes
ugh jquery
2021-11-03 16:47:55 -05:00
Lance Edgar 209b4b4de3 Update changelog 2021-11-02 11:15:44 -05:00
Lance Edgar 7651efff9d Highlight "cannot calculate price" rows for new product batch 2021-10-31 11:56:46 -05:00
Lance Edgar 7b5e2d17f3 Omit "edit" link unless user has perm, for Customer "people" subgrid 2021-10-28 19:00:56 -05:00
Lance Edgar 4dfc29768c Improve validation for Person field of User form
otherwise if user enters e.g. "John Doe" but does *not* select an
autocomplete result, then "John Doe" will be submitted as-is to the
server, which then tried to write that directly to
``users.person_uuid`` column in the DB, resulting in error
2021-10-28 18:55:28 -05:00
Lance Edgar 2d87ce5c29 Highlight the "did not receive" rows for purchase batch
also add some row grid links
2021-10-22 21:53:46 -05:00
Lance Edgar 2d0a922cff Show case qty by default for costing batch rows 2021-10-22 21:24:08 -05:00
Lance Edgar a553a26644 Optionally set the sticky-header attribute for main buefy grids
should affect the 'index' and 'view' (with rows) but i don't think any
other pages will get this..?
2021-10-22 21:04:39 -05:00
Lance Edgar 4a383709bd Update changelog 2021-10-20 16:15:19 -05:00
Lance Edgar 8d16a5f110 Clean up the product selection UI for new custorder
still needs some work but this is much better, more like the customer
selection now w/ "multi-faceted" autocomplete
2021-10-20 07:26:05 -05:00
Lance Edgar 8b044dbb22 Add basic "price needs confirmation" support for custorder 2021-10-18 18:28:28 -05:00
Lance Edgar 93b752f436 Invoke handler when adding new item to custorder batch 2021-10-17 18:07:57 -04:00
Lance Edgar 87374d5647 Fix auth handler reference bug 2021-10-17 17:29:26 -04:00
Lance Edgar ab33b49218 Improve "refresh contact", show new fields in green for custorder
only showing new "customer" fields in green so far
2021-10-17 17:28:28 -04:00
Lance Edgar 52fbe73893 Overhaul the autocomplete component, for sake of new custorder
turns out we had some issues with our understanding of how that all
was supposed to work.  this seems to be much cleaner and even
semi-documented :)
2021-10-16 15:37:23 -04:00
Lance Edgar 232a02b944 Refactor to leverage all existing methods of auth handler
instead of importing and calling functions from core rattail
2021-10-14 23:30:26 -04:00
Lance Edgar 53fc1508f3 Give custorder batch handler a couple ways to affect adding new items 2021-10-14 17:49:12 -04:00
Lance Edgar 1b33c8a2b7 Update changelog 2021-10-14 14:22:07 -04:00
Lance Edgar dd6c9cc8ce Misc. tweaks for users, roles 2021-10-14 14:18:36 -04:00
Lance Edgar d61fa7b6b9 Update changelog 2021-10-14 12:12:10 -04:00
Lance Edgar 22aa55c24b Invoke the auth handler to cache user permissions etc.
various changes for sake of "synced" roles feature
2021-10-14 10:39:54 -04:00
Lance Edgar 80589cde2f Cleanup form display a bit, for App Settings 2021-10-13 17:29:41 -04:00
Lance Edgar 1463c09385 Update changelog 2021-10-13 12:19:49 -04:00
Lance Edgar e3cad91be0 Leverage the auth handler for main user login 2021-10-12 18:22:04 -04:00
Lance Edgar aeace0c7cf Add debounce() wrapper for buefy autocomplete
per docs, although was not very clear "which" debounce i needed, this
one at least works without errors..

hoping this fixes some page performance issues when tailbone
autocomplete component is present
2021-10-12 14:17:10 -04:00
Lance Edgar 20492410ad Update changelog 2021-10-11 21:58:18 -04:00
Lance Edgar 66bc775e14 Improve display, handling for "add contact info to customer record"
for new custorders page.  in particular, show this flag in main screen
2021-10-10 20:43:27 -04:00
Lance Edgar 3e796e9164 Fix bug when making context for mailing address
sometimes those belong to a non-person, e.g. customer
2021-10-10 20:24:26 -04:00
Lance Edgar ffb33d00c8 Fix some phone/email bugs for new custorder page 2021-10-10 20:21:41 -04:00
Lance Edgar 7fabef6004 Stop rounding case/unit cost fields to 2 places for purchase batch 2021-10-10 20:08:52 -04:00
Lance Edgar ce969306f7 Update changelog 2021-10-10 18:42:46 -04:00
Lance Edgar a919bfb6c5 Simplify template context customization for view_profile_buefy 2021-10-07 21:13:59 -04:00
Lance Edgar b9b5a0e79b Update changelog 2021-10-07 19:36:17 -04:00
Lance Edgar 284078ff71 Delete pending customer if deleting custorder batch
also invoke handler to update pending customer info for batch, so the
handler can add validation, e.g. unique email address check
2021-10-07 13:08:48 -04:00
Lance Edgar 5e339bb7ea Improve contact name handling for new custorder 2021-10-07 12:33:52 -04:00
Lance Edgar c611eb3787 Clear out contact for custorder if user clicks "customer is unknown"
also show pending customer reference when viewing proper custorder
2021-10-06 18:43:52 -04:00
Lance Edgar d933dd2723 Add support for "new customer" when creating new custorder 2021-10-06 18:22:29 -04:00
Lance Edgar 25a019cc12 Update changelog 2021-10-06 14:55:19 -04:00
Lance Edgar 9b40096bb7 Add "contact update request" workflow for new custorder batch
if user checks "please add phone to customer record" etc. then this
preference is stored in the batch params, and when batch is executed
that will "happen" (which may just mean someone gets email about it)
2021-10-06 14:49:13 -04:00
Lance Edgar 2fa7857daf Add "allow contact info choice" support for new custorder batch 2021-10-06 12:43:38 -04:00
Lance Edgar 0237d8c31a Add "restrict contact info" feature for new custorder batch
also add support for choosing from existing emails
2021-10-06 12:32:13 -04:00
Lance Edgar 9b6113a4c8 Show shipped quantities when viewing costing batch row
for lines which came from invoice, we should know those quantities,
but possibly *not* the received quantities, if e.g. the line item
wasn't matched w/ PO
2021-10-05 16:20:08 -04:00
Lance Edgar def8ea7c15 Some tweaks for invoice costing batch views 2021-10-05 16:12:48 -04:00
Lance Edgar d7c145ce39 Update changelog 2021-10-05 10:43:17 -04:00
Lance Edgar e7fb1559f5 Refactor the Employee tab of profile view, per better patterns
learned some things from the Personal tab overhaul
2021-10-05 08:25:33 -04:00
Lance Edgar 6386b34516 Overhaul the "Personal" tab of profile view
should be much more useful now.. er, at least for those who track
contact info on the Person record, but not those who track on the
Customer record..
2021-10-04 21:21:34 -04:00
Lance Edgar 48864ab611 Put the View Profile button above Refresh 2021-10-04 12:40:35 -04:00
Lance Edgar 8e4079224f Add button to refresh contact info for new custorder
e.g. click that after changes are made in other screen / system
2021-10-04 12:39:30 -04:00
Lance Edgar d4aef9ceac Fix contact phones data when new contact is assigned 2021-10-04 12:29:27 -04:00
Lance Edgar 1884edb334 Improve phone editing for new custorder
let user choose from existing phones, or add a new one.  not yet
implemented, they can check a box to add new phone to customer proper
in addition to setting it for the order
2021-10-04 12:25:41 -04:00
Lance Edgar 711e526822 Show "contact notes" when creating new custorder 2021-10-03 19:26:25 -04:00
Lance Edgar 272b0fd071 Update changelog 2021-10-01 18:38:24 -04:00
Lance Edgar a7f4b2e6ef Refactor autocomplete view logic to leverage new "autocompleters"
finally!  this cleans up some view config and AFAIK there is no loss
in functionality etc.
2021-10-01 10:28:06 -04:00
Lance Edgar e0dff55ffa Update changelog 2021-09-30 16:34:56 -04:00
Lance Edgar b2e2b2e85e Fix one broken test; remove another
ugh what are these tests even accomplishing..
2021-09-30 16:17:55 -04:00
Lance Edgar bbfffd45fc Initial (basic) views for invoice costing batches
still a bit of feature preview at the moment, but maybe is mostly done?
2021-09-29 17:27:20 -04:00
Lance Edgar ed705ff867 Update changelog 2021-09-28 16:15:38 -04:00
Lance Edgar 03a569d9a3 Avoid "detach person" logic if not supported by view class 2021-09-28 16:12:33 -04:00
Lance Edgar a6c89d7998 Show "missing" msg if no email, for new custorder 2021-09-28 16:10:04 -04:00
Lance Edgar ad6562558d Improve phone/email handling when making new custorder
still needs more improvement, but this is a start
2021-09-27 18:04:07 -04:00
Lance Edgar 82074a37ba Update changelog 2021-09-27 13:28:26 -04:00
Lance Edgar ab517d1199 Allow changing status, adding notes for customer order items 2021-09-27 13:25:02 -04:00
Lance Edgar 7c6c2f7ded Update changelog 2021-09-27 09:54:34 -04:00
Lance Edgar 65ac7e0c15 Add a dropdown of choices to the Department filter for Products grid 2021-09-27 09:46:31 -04:00
Lance Edgar a52b5ec380 Overhaul new custorder so contact may be either Person or Customer
also make the handler responsible for (un)assigning contact
2021-09-27 09:22:06 -04:00
Lance Edgar 12310da09e Update changelog 2021-09-26 17:26:11 -04:00
Lance Edgar 8095f2c9ea Display the Store field for Customer Orders 2021-09-25 18:55:53 -04:00
Lance Edgar 3ece3303db Refactor several "field grids" per Buefy theme
e.g. the Users field when viewing a Role, and Vendor Sources panel
when viewing a Product
2021-09-25 18:54:33 -04:00
Lance Edgar 9fe1d4c596 Update changelog 2021-09-25 15:34:29 -04:00
Lance Edgar 0dc9793772 Add products row grid for misc. org table views 2021-09-25 15:27:43 -04:00
Lance Edgar ec5ff8a788 Improve "employees" list when viewing a department, for buefy themes 2021-09-24 19:16:23 -04:00
Lance Edgar 3b6b1aa5b6 Invoke handler for customer autocomplete when making new custorder 2021-09-24 18:28:39 -04:00
Lance Edgar 57cb787b30 Add placeholder to customer lookup for new order
also hide phone field unless customer is identified
2021-09-24 17:28:14 -04:00
Lance Edgar fbd12c7dfc Improve default autocomplete query logic, w/ multiple ILIKE
e.g. to search for customer first and/or last name
2021-09-24 17:17:19 -04:00
Lance Edgar e6a92c5667 Update changelog 2021-09-22 18:30:39 -05:00
Lance Edgar 9365dd7b1a Add way to update Employee ID from profile view 2021-09-22 18:29:30 -05:00
Lance Edgar af8bd246a9 Update changelog 2021-09-22 16:50:17 -05:00
Lance Edgar 87d8322b85 Add way to override grid action label rendering
so that custom HTML can be embedded in there somehow..
2021-09-22 16:42:49 -05:00
Lance Edgar b229b409b0 Update changelog 2021-09-21 13:52:49 -05:00
Lance Edgar d0a7a241b4 Misc. improvements for customer order views 2021-09-21 13:49:51 -05:00
Lance Edgar 8af247a7f6 Update changelog 2021-09-19 19:08:53 -05:00
Lance Edgar d295cf04af Allow setting the "exclusive" sequence of grid filters
i.e. let caller specify that any not included, should be omitted
2021-09-19 18:36:25 -05:00
Lance Edgar 2188e91fae Update changelog 2021-09-16 11:10:21 -05:00
Lance Edgar 884b1e02a7 Invoke handler when request is made to merge 2 people 2021-09-15 19:01:53 -05:00
Lance Edgar 7e0713e22b Update changelog 2021-09-12 19:14:52 -05:00
Lance Edgar 25c1ae3c41 Add way to customize product autocomplete for new custorder 2021-09-09 19:15:08 -05:00
Lance Edgar 177286533d Update changelog 2021-09-09 17:22:00 -05:00
Lance Edgar 83c354b983 Set quantity type when viewing vendor lead times, order intervals 2021-09-09 17:07:46 -05:00
Lance Edgar 1ce60821bd Update changelog 2021-09-09 16:23:27 -05:00
Lance Edgar 97bdc3f785 Improve error handling for purchase batch
so error will display in browser when applicable
2021-09-09 12:00:13 -05:00
Lance Edgar 82e730c18e Add the Grid.remove() method, deprecate hide_column() etc.
this is more clear, and aligns with how Form works
2021-09-08 14:33:40 -05:00
Lance Edgar 4474f30718 Allow override of "create" permission in API 2021-09-03 18:26:55 -05:00
Lance Edgar fa700d53ad Add /people API endpoint; allow for "native sort" 2021-09-03 16:26:15 -05:00
Lance Edgar d671b18215 Update changelog 2021-09-01 12:20:45 -05:00
Lance Edgar 8169160b57 Allow "touch" action for employees 2021-08-31 22:05:02 -05:00
Lance Edgar 560575e53f Fix size of roles multi-select when editing user
i.e. for buefy themes
2021-08-31 22:04:37 -05:00
Lance Edgar 54f1a52ed0 Add hover text for vendor ID column of pricing batch row grid 2021-08-29 19:52:44 -05:00
Lance Edgar c2ea1be83f Improve UI, customization hooks for new custorder batch
still not done yet, but a savepoint
2021-08-29 16:38:30 -05:00
Lance Edgar 4d742bacb1 Allow grid columns to be *invisible* (but still present in grid)
this can be useful when you need contextual data for a given row, for
sake of front-end UI features, but do not want to actually show the
extra data column(s)
2021-08-29 10:28:36 -05:00
Lance Edgar fe584f193f Always show all grid actions...for now
we don't have a great way to accommodate too many actions; ideally
could hide some in a drawer, but for now we just show them all for
simplicity...
2021-08-28 18:45:31 -05:00
Lance Edgar 897bb177bc Make it easier to override rendering grid component in master/index
was needed so i could pass extra event handlers to it
2021-08-28 14:24:56 -05:00
Lance Edgar 445862d48d Update changelog 2021-08-26 11:55:09 -05:00
Lance Edgar c3079fe899 Add before_render_index() customization hook for MasterView 2021-08-24 09:39:45 -05:00
Lance Edgar 3cf4c0f8e4 Require explicit opt-in for "clicking grid row checks box" feature
sometimes it makes sense *not* to enable that, in which case disabled
probably should be the default
2021-08-23 19:26:50 -05:00
Lance Edgar a881b310bc Allow customization of row 'view' action url 2021-08-23 14:25:08 -05:00
Lance Edgar ac133ce830 Expose "merge request tracking" feature for People data
more to come i'm sure, but this covers the basics
2021-08-19 18:11:15 -05:00
Lance Edgar cf32d4235e Move "merge 2 people" logic into People Handler
view now delegates to handler, which lives in the rattail package
2021-08-18 19:21:21 -05:00
Lance Edgar 5836099746 Tweak how email preview is sent, and attempt "to" is displayed
latter only have been changed for the grid view.  preview now is sent
"properly" via the configured mail handler, which also means that an
attempt may be recorded (whereas previously it would not be)
2021-08-16 19:29:48 -05:00
Lance Edgar a10de791a1 Update changelog 2021-08-04 13:01:09 -05:00
Lance Edgar 90af8f91b8 Let feedback forms define their own email key
so multiple recipient options may be presented to user, e.g. in public
frontend
2021-08-02 18:26:15 -05:00
Lance Edgar 8884d28306 Update changelog 2021-07-15 14:15:19 -05:00
Lance Edgar 4addedef6e Expose pack_size for delproduct batch 2021-07-15 14:13:01 -05:00
Lance Edgar 8eee4a1cf0 Set UPC renderer for delproduct batch row 2021-07-15 13:29:31 -05:00
Lance Edgar fb156d2e29 Update changelog 2021-06-18 17:53:27 -05:00
Lance Edgar 35aab87fdc Include "is/not null" filters for GPC fields 2021-06-18 17:39:14 -05:00
Lance Edgar 5cdd09020d Update changelog 2021-06-15 21:35:58 -05:00
Lance Edgar 2e561f1a4a Add 'v' prefix for release package diff links
at least i think that is needed...
2021-06-15 21:34:22 -05:00
Lance Edgar a1d6403b1b Update changelog 2021-06-15 15:51:57 -05:00
Lance Edgar b2bda5e31d Allow config to set favicon and header image
it already could set "main" image, shown in home and login pages
2021-06-15 15:51:11 -05:00
Lance Edgar add4337d11 Update changelog 2021-06-11 13:34:40 -05:00
Lance Edgar 31941c00bf Allow generating project which integrates w/ LOC SMS 2021-05-24 16:21:08 -05:00
Lance Edgar d1a35a4d58 Allow direct creation of new label batches
now technically this is allowed on desktop, but probably makes more
sense on mobile via api
2021-05-18 12:46:45 -05:00
Lance Edgar 949b9d64bf Allow customization of rendering version diff values 2021-05-14 12:13:23 -05:00
Lance Edgar 00615bea97 Update changelog 2021-05-03 12:36:41 -05:00
Lance Edgar 91db10b10c Tweak tox config a bit per broken tests 2021-04-28 19:07:42 -05:00
Lance Edgar eede391be8 Freeze pyramid version at 1.x
we need to get to python3 before can get latest cornice, and until then we
can't get latest pyramid either
2021-04-28 19:07:15 -05:00
Lance Edgar 544f05a5a8 Add csrftoken to TailboneForm js
ugh..for now at least
2021-04-28 14:07:24 -05:00
Lance Edgar 661d536e9d Highlight "has inventory" rows for delete item batch
also pass list of such rows to template context
2021-04-28 14:06:35 -05:00
Lance Edgar 60fe7cf29c Update changelog 2021-04-12 11:52:54 -05:00
Lance Edgar 2d75409757 Accept any decimal numbers for API inventory batch counts
i.e. don't assume integer values
2021-04-12 11:36:24 -05:00
Lance Edgar c48371ca2a Make it easier to extend "common" API views 2021-04-07 17:04:52 -05:00
Lance Edgar 6c5377fadc Show current price date range as hover text, for products grid 2021-04-07 12:29:33 -05:00
Lance Edgar 4cf61a92cf Update changelog 2021-03-30 11:50:20 -05:00
Lance Edgar 2332cae09b Expose the new Store.archived flag 2021-03-19 10:39:25 -05:00
Lance Edgar ee65d08d81 Catch and show error, if one happens when making batch from product query 2021-03-19 10:38:56 -05:00
Lance Edgar e19119194d Update changelog 2021-03-11 11:49:18 -06:00
Lance Edgar e4e0d81f6e Fix enum display for customer order status 2021-03-11 08:47:27 -06:00
Lance Edgar 70c5e36ccb Expose new fields for Trainwreck 2021-03-10 07:25:25 -06:00
Lance Edgar 7532dc5117 Add support for inactivity_months field for delete product batch 2021-03-09 11:44:56 -06:00
Lance Edgar 059b24fac7 Update changelog 2021-03-05 12:51:55 -06:00
Lance Edgar 97e1700cf9 Expose date_created for delete product batches 2021-03-05 12:51:55 -06:00
Lance Edgar 241747b967 Allow per-user stylesheet for Buefy themes
there is not yet a way for user to select from available options though
2021-03-05 12:02:32 -06:00
Lance Edgar a933fc836f Update changelog 2021-03-02 09:30:41 -06:00
Lance Edgar 492546d0f6 Add hover text for subdepartment name, in pricing batch row grid 2021-03-02 09:26:36 -06:00
Lance Edgar ba790823ed Highlight delete product batch rows with "pending customer orders" status 2021-03-01 17:34:24 -06:00
Lance Edgar 637c249c36 Handle row click as if checkbox was clicked, for checkable grid
should be more convenient since the checkbox is a rather small target as
compared to the row itself.

this also brings in newer Buefy 0.8.6 b/c it includes "shift+click" behavior
for the checkbox:

- https://github.com/buefy/buefy/issues/535
- https://github.com/buefy/buefy/pull/1894
2021-02-26 21:49:58 -06:00
Lance Edgar abfe8bc648 Fix message recipients for Reply / Reply-All, with Buefy themes 2021-02-24 17:53:48 -06:00
Lance Edgar 216807503a Avoid encoding values as string, for integer grid filters
grid filter for Catapult Transaction "Status" was not working right b/c that is
an integer in the db, but we were passing encoded string value to SA / query
2021-02-20 08:45:15 -06:00
Lance Edgar 89bb0aa56d Use end time as default filter, sort for Trainwreck 2021-02-18 20:02:53 -06:00
Lance Edgar 26d7ab080f Update changelog 2021-02-18 11:51:05 -06:00
Lance Edgar 9ad64ba5e1 Add support for "default" trainwreck model
per rattail changes
2021-02-17 20:18:45 -06:00
Lance Edgar 793022b92f Misc. tweaks for vendor catalog batch
per rattail changes, in particular for sake of Corporal, to allow for
non-native vendor and product associations
2021-02-15 12:57:35 -06:00
Lance Edgar ff904d840f Tweaks per "delete products" batch 2021-02-13 12:29:43 -06:00
Lance Edgar 34623a7307 Add special "equal to any of" verb for UPC-related grid filters 2021-02-12 14:05:44 -06:00
Lance Edgar 89f0336af9 Add special "contains any of" verb for string-based grid filters 2021-02-12 13:57:54 -06:00
Lance Edgar 1420a33649 Allow customization of main Buefy CSS styles, for falafel theme 2021-02-11 15:57:18 -06:00
Lance Edgar a23eb3f32d Update changelog 2021-02-10 11:53:40 -06:00
Lance Edgar eaf929474f Add styles for field labels in profile view 2021-02-10 11:35:05 -06:00
Lance Edgar f58b065316 Make errors more obvious, when running batch commands as subprocess
admin still must consult logs to determine cause, but at least UI won't hang
2021-02-09 14:24:05 -06:00
Lance Edgar e462e41ae1 Cleanup default display for vendor catalog batches
expose description, notes etc.
2021-02-09 14:22:07 -06:00
Lance Edgar 5969515f25 Render batch execution description as markdown 2021-02-09 14:21:07 -06:00
Lance Edgar cc2308c399 Fix some permission bugs when showing batch tools etc. 2021-02-09 12:19:26 -06:00
Lance Edgar 85403dfa5e Update changelog 2021-02-04 16:45:24 -06:00
Lance Edgar 8f69b07ee2 Fix bug when editing a Person 2021-02-04 16:44:47 -06:00
Lance Edgar 562d7b48bc Update changelog 2021-02-04 11:04:00 -06:00
Lance Edgar 63350469d0 Prevent updates to batch rows, if batch is immutable
probably need a lot more support for this elsewhere; this is all i needed for
the moment though..
2021-02-02 18:58:46 -06:00
Lance Edgar f93fd7aefa Fix display of handheld batch links, when viewing label batch 2021-02-02 14:48:34 -06:00
Lance Edgar 0128690da8 Update references to vendor invoice batches
per table/model rename
2021-02-02 13:49:10 -06:00
Lance Edgar 9b76e23354 Update references to vendor catalog batches
per table/model rename
2021-02-02 13:28:56 -06:00
Lance Edgar e3bf7f2bb2 Add stub methods for MasterView.template_kwargs_view() etc.
otherwise subclass has to consider, can i call super() or not?  it still does
for some other views, but at least create/view/edit are common enough that it
should always be able to call super() without concern for those
2021-02-02 10:57:58 -06:00
Lance Edgar 0209957def Fix config defaults for PurchaseView
so can customize that more easily
2021-02-01 17:15:39 -06:00
Lance Edgar 1cdb11c88c Update changelog 2021-02-01 13:59:37 -06:00
Lance Edgar 8e9c66c0ea Add icon for Help button 2021-02-01 13:58:10 -06:00
Lance Edgar fe80028c07 Add support for "nested" menu items
some menus were just getting too long, so this gives us a way to collapse
certain items, which user can expand as needed
2021-02-01 13:58:08 -06:00
Lance Edgar 329e75ee82 Add initial "scanning" feature for Ordering Batches 2021-01-31 21:51:46 -06:00
Lance Edgar 801c56f06e More tweaks for receiving batch workflows
now first step requires choice of vendor and workflow.  supports receiving from
PO at least for native use case.
2021-01-31 12:10:44 -06:00
Lance Edgar a2b7f882bc Split "new receiving batch" process into 2 steps: choose, create
so that the form used to create the batch can be made custom per-workflow, and
it won't have to think about any other workflows since we just use one form at
a time for that
2021-01-30 19:54:38 -06:00
Lance Edgar ff2e39f67a Make handler responsible for possible receiving modes 2021-01-30 16:56:30 -06:00
Lance Edgar 708641a8f1 Purge things for legacy (jquery) mobile, and unused template themes
gosh it feels good to get rid of this stuff...  fingers crossed that nothing
was broken, but am thinking it's safe
2021-01-30 15:52:47 -06:00
Lance Edgar fac00e6ecd Misc. improvements for ordering batches, purchases
also we now show handler's description when executing batch
2021-01-30 13:17:08 -06:00
Lance Edgar e1e3301fc1 Undo recent base.css changes for <p> tags
turns out i should be doing `<p class="block">` when i want spacing
2021-01-30 13:12:04 -06:00
Lance Edgar 1a18147971 Normalize naming of all traditional master views
whoops, missed one..
2021-01-28 17:18:45 -06:00
Lance Edgar 719e7c8441 Normalize naming of all traditional master views
such names should never use plural forms.  for now what plural forms were
previously in use, should still work.  ideally can remove those at some point
2021-01-28 16:32:25 -06:00
Lance Edgar 3ad19d05e5 Update changelog 2021-01-28 14:56:13 -06:00
Lance Edgar fb7a572519 Use "People Handler" to update names, when editing person or user 2021-01-28 14:34:18 -06:00
Lance Edgar b3867d9c89 Tweak how vendor link is rendered for readonly field 2021-01-27 22:24:23 -06:00
Lance Edgar 797a65e9c8 Update changelog 2021-01-27 14:16:11 -06:00
Lance Edgar 40b4596df4 Hopefully fix package links for upgrade diff
why in the F doesn't a hyphen work for this?
2021-01-27 09:01:42 -06:00
Lance Edgar 5e27ceedce Hide "collect from wild" button for UOMs unless user has permission 2021-01-27 08:56:38 -06:00
Lance Edgar a927827e33 Add changelog link for Theo, in upgrade package diff 2021-01-27 08:52:38 -06:00
Lance Edgar d1d64ec96c Fix some UOM bugs for new customer order 2021-01-27 08:50:20 -06:00
Lance Edgar 480d878db8 Initial support for adding items to, executing customer order batch 2021-01-26 20:10:05 -06:00
Lance Edgar 475ab3013f Update changelog 2021-01-25 11:43:35 -06:00
Lance Edgar b55ecc3898 Tweak label style, per recent base.css change 2021-01-23 21:08:00 -06:00
Lance Edgar a327dfab7c Add basic web API app, for simple use cases
plus some functions which make it easier to customize
2021-01-23 14:11:05 -06:00
Lance Edgar 649ac12cdd Add woocommerce package links for sake of upgrade diff view 2021-01-21 17:48:09 -06:00
Lance Edgar dde6195f38 Add master view for Units of Measure mapping table
w/ support for "collect from wild" tool
2021-01-21 17:39:16 -06:00
Lance Edgar 0035a4129a Add custom base.css for falafel theme
this copies from bobcat/base.css and just adds margin-bottom for p tag.

this was done b/c in certain Buefy dialogs etc. the p tags are too close
together.  not sure if this change breaks anything else yet...
2021-01-21 17:37:17 -06:00
Lance Edgar 523ea6e0df Add some generic render methods to MasterView 2021-01-20 21:56:20 -06:00
Lance Edgar 59167278d4 Set self.model when constructing new View 2021-01-20 20:29:07 -06:00
Lance Edgar f480c046f6 Add views for "delete product" batch 2021-01-19 12:18:56 -06:00
Lance Edgar af99ca7905 Make 'new-report' the default feature to be generated 2021-01-19 11:25:02 -06:00
Lance Edgar 850b6f71dd Add basic support for defining columns when generating new table feature 2021-01-18 00:32:30 -06:00
Lance Edgar ca602ff845 Add feature to generate new features...
at least that's the idea.  guess we'll see where this goes
2021-01-17 12:08:33 -06:00
Lance Edgar ce629c91bb Add CSS styles for 'codehilite' a la Pygments
this is in anticipation for displaying syntax-highlighted code snippets from
markdown source.  this CSS file was generated according to instructions at
https://python-markdown.github.io/extensions/code_hilite/
2021-01-17 11:15:24 -06:00
Lance Edgar a3cbb24892 Add variant transaction logic per zope.sqlalchemy 1.1 changes
without this we can't use zope.sqlalchemy 1.1 due to error
2021-01-16 14:13:34 -06:00
Lance Edgar db7d021133 Allow newer zope.sqlalchemy package
not sure of any real benefit, but could not find any reason to cap at such an
old version, so let's relax it
2021-01-14 12:47:37 -06:00
Lance Edgar 5e9264bbef Don't create new person for new user, if one was selected 2021-01-14 12:10:35 -06:00
Lance Edgar 758d5e6f4c Update changelog 2021-01-10 21:24:59 -06:00
Lance Edgar 4d8e29c892 Add basic support for "download" and "rawbytes" API views 2021-01-06 13:12:27 -06:00
Lance Edgar fd1342c605 Try to show existing filename, for upload widget 2021-01-05 18:53:00 -06:00
Lance Edgar e548b72323 Fix some deform template comments 2021-01-05 18:19:27 -06:00
Lance Edgar ad859d4bef Allow specifying the size of a file, for readable_size() method
sometimes the file bytes are stored in DB instead of on disk
2021-01-04 13:22:44 -06:00
Lance Edgar 483a47ed43 Allow changing protected user password when acting as root 2021-01-02 18:49:20 -06:00
Lance Edgar 9c026c1dd9 Show node title in header for Login, About pages 2021-01-02 18:48:45 -06:00
Lance Edgar 6a0bcdaa82 Update changelog 2020-12-16 14:53:17 -06:00
Lance Edgar cc833c52b6 Add common "form poster" logic, to make CSRF token/header names configurable
also refactor the Feedback logic to use it
2020-12-16 14:28:41 -06:00
Lance Edgar a801672821 Improve error handling for feedback form
also make sure the message doesn't self-destruct when closing the dialog
2020-12-16 12:47:45 -06:00
Lance Edgar 20f3d001c4 Update changelog 2020-12-15 20:08:02 -06:00
Lance Edgar 058677adec Tweak spacing for header logo + title, in falafel theme
those were just too close together, this should fix.

nb. i am unclear if everything in layout.css is actually being used..?
2020-12-15 19:09:17 -06:00
Lance Edgar 95dd8d83dc Hopefully temporary version cap for deform
getting the following error w/ v2.0.15:

```
  File "/srv/envs/XXX/lib/python3.7/site-packages/deform/field.py", line 452, in get_widget_requirements
    requirements = [req for req in self.widget.requirements] + [
TypeError: 'NoneType' object is not iterable
```
2020-12-14 15:13:17 -06:00
Lance Edgar 8ff590e43f Expose "commodity" filter by default, for IFPS PLU codes 2020-12-07 19:01:43 -06:00
Lance Edgar 42eb72422d Add very basic support for merging 2 People
this is not very complete, but was enough for what i needed at the moment.
almost seems like incomplete feature may be worse than none at all?  but then
again some sort of default starting point is nice i guess...
2020-12-07 11:40:26 -06:00
Lance Edgar ac5139b7c4 Add basic views for IFPS PLU Codes 2020-12-06 19:36:32 -06:00
Lance Edgar 3250347df1 Add sqlalchemy version cap for tox coverage, docs
this is of course just kicking the can down the road a bit for now...really
need to get the latest zope.sqlalchemy instead...
2020-12-04 18:46:58 -06:00
Lance Edgar 2d8d4659b3 Use python3 when building coverage, docs targest via tox
at least i think that's what this does..hopefully it works
2020-12-04 18:27:47 -06:00
Lance Edgar efbc6df199 Tweak tox test config for py27
to make buildbot happy...
2020-12-04 18:06:53 -06:00
Lance Edgar 3ae47ba1e5 Update changelog 2020-12-04 17:50:56 -06:00
Lance Edgar a204e78e3a Assume composite PK when fetching instance for master view
i.e. stop trying a simple get() which would assume not only a simple PK, but
also assumes the PK is same as defined by the class mapper.  in some cases it
may be helpful to use a different PK from what mapper defines
2020-12-04 15:26:50 -06:00
Lance Edgar 0220e401cd Add "is empty" and related verbs, for "string" type grid filters 2020-12-04 15:26:21 -06:00
Lance Edgar 2ad0223e9a Add the "Employee Status" filter to People grid 2020-12-02 14:03:19 -06:00
Lance Edgar 04ba14fcd7 Update changelog 2020-12-01 20:05:19 -06:00
Lance Edgar e5d5850327 Add "generic" Employee tab feature, for profile view
i.e. this now exposes a way to begin/end employment status for a person, and
invokes the "employment handler" accordingly
2020-11-30 18:56:06 -06:00
Lance Edgar c87a452471 Tweak how an "enum" grid filter is initialized
wasn't working quite right for Buefy theme
2020-11-25 18:49:02 -06:00
Lance Edgar 3cd5fa7f4a Misc. tweaks to vendor catalog views
for sake of titeship
2020-10-23 22:08:43 -05:00
Lance Edgar ee3d32d60a Update changelog 2020-10-13 16:59:54 -05:00
Lance Edgar d80844c1ed Tweak how global DB session is created
no need to specify "record changes" flag here
2020-10-13 16:59:51 -05:00
Lance Edgar 9af7e38219 Update changelog 2020-09-29 18:07:10 -05:00
Lance Edgar dc1f613bc2 Fix "refresh results" for batches, in Buefy theme 2020-09-28 13:23:01 -05:00
Lance Edgar e0d1e39824 Add feature to "download rows for results" in master index view 2020-09-28 12:45:46 -05:00
Lance Edgar bcb4bda7e6 Fix bug in App Settings when list value is "missing" 2020-09-26 15:00:42 -05:00
Lance Edgar 37a05155e5 Add support for "list" type of app settings (w/ textarea) 2020-09-25 23:23:01 -05:00
Lance Edgar 18b9f43eaa Update changelog 2020-09-25 17:55:39 -05:00
Lance Edgar 20c31cbb07 Fix grid bug when paginator is not involved 2020-09-25 16:05:07 -05:00
Lance Edgar 5b05f9426f Allow alternate engine to act as 'default' when multiple are available 2020-09-25 16:04:32 -05:00
Lance Edgar 9dc9bd162f Update changelog 2020-09-24 13:54:46 -05:00
Lance Edgar c79b63e270 Fix bug when fetching partial versions data grid
e.g. when requesting new page of data
2020-09-23 20:42:43 -05:00
Lance Edgar 2d699b3e43 Add global help URL to login template 2020-09-23 18:32:53 -05:00
Lance Edgar 24cc4b4272 Change how we protect certain person, employee records 2020-09-23 16:39:44 -05:00
Lance Edgar 746db72046 Add user_is_protected() method to core View class
also, don't allow "protected" users to change their own password
2020-09-23 16:28:54 -05:00
Lance Edgar 77fa2a78d4 Update changelog 2020-09-22 19:40:47 -05:00
Lance Edgar af11511d24 Remove unwanted columns for default Products grid 2020-09-20 23:35:07 -05:00
Lance Edgar 6709d97abc Only show node title in home page header, for buefy themes
it's just redundant for the old jquery theme
2020-09-20 19:57:27 -05:00
Lance Edgar 4b4faae009 Show node title in header, for home page 2020-09-20 19:55:33 -05:00
Lance Edgar f37a9963f6 Add some more field hints when generating 'fabric' project 2020-09-20 18:04:44 -05:00
Lance Edgar 2d29245037 Don't expose "timezone" for input when generating 'fabric' project
static default is good enough for that
2020-09-20 18:01:23 -05:00
Lance Edgar d146514c39 Prompt user if they try to send email preview w/ no address 2020-09-20 17:41:04 -05:00
Lance Edgar 149ae4b71c Avoid unhelpful error when perm check happens for "re-created" DB user
kind of an edge case, should only apply to dev
2020-09-20 16:32:44 -05:00
Lance Edgar 711ed947a3 Add "worksheet file" pattern for editing batches
lets user download a worksheet, edit, then upload back to update the batch
2020-09-18 12:17:04 -05:00
Lance Edgar 37a60592f6 Add 'warning' class for 'delete' action in b-table grid 2020-09-17 13:54:43 -05:00
Lance Edgar 3ac8ca90ce Update changelog 2020-09-16 22:23:28 -05:00
Lance Edgar cc5d0ed3c6 Tweak option label for Catapult when generating project 2020-09-16 22:22:37 -05:00
Lance Edgar 652e951f89 Add support for generating new 'fabric' project 2020-09-16 19:25:03 -05:00
Lance Edgar dd2b634ed2 Remove some custom field labels for Vendor
should use `labels` dict if really needed, but they don't seem to be
2020-09-15 18:34:00 -05:00
Lance Edgar 32cfe58601 Allow custom props for TailboneForm component 2020-09-15 09:33:27 -05:00
Lance Edgar e6da1152ca Update changelog 2020-09-14 13:10:12 -05:00
Lance Edgar 3eb929aa13 Hide the 'byjove' option for generating new project
until we actually support it
2020-09-08 19:05:36 -05:00
Lance Edgar 7df5838bc0 Require permission to generate a new project 2020-09-08 19:01:34 -05:00
Lance Edgar f8d9b0803c Add some help text to new project form, etc. 2020-09-08 14:10:49 -05:00
Lance Edgar cf613ab34a Split "new project" forms into multiple sections 2020-09-06 14:47:14 -05:00
Lance Edgar cebe2f8adc Add basic/incomplete support for generating new 'byjove' project
just wanted to get the placeholder in here for now
2020-09-06 13:54:11 -05:00
Lance Edgar bd19d7c231 Add view for generating new project from template
this was copied as-is from titeship
2020-09-06 12:36:20 -05:00
Lance Edgar 1283a794df Overhaul project changelog links for upgrade pkg diff table 2020-09-05 17:48:34 -05:00
Lance Edgar fdcf23f65f Stop including 'complete' filter by default for purchasing batches 2020-09-04 20:30:33 -05:00
Lance Edgar 24516b81cb Update changelog 2020-09-02 13:44:13 -05:00
Lance Edgar 527bc04998 Expose Product.average_weight field 2020-09-02 13:38:18 -05:00
Lance Edgar 72177e8ab5 Improve auto-handling of "local" timestamps
for non-Rattail DBs where timestamps are local instead of UTC
2020-09-02 11:30:02 -05:00
Lance Edgar d2d632092b Add support for batch execution options in Buefy themes
i.e. from "view batch" page
2020-08-23 13:53:55 -05:00
Lance Edgar 7a0f975b31 Fix spacing between components in "grid tools" section 2020-08-23 11:26:29 -05:00
Lance Edgar 026dc6309c Fix "execute results" batch template logic for Buefy themes 2020-08-23 11:23:22 -05:00
Lance Edgar 5af26a57f6 Improve layout for "download results" modal 2020-08-22 16:11:29 -05:00
Lance Edgar 922cbe4451 Add new/flexible "download results" feature 2020-08-22 15:24:32 -05:00
Lance Edgar 43472c7eb6 Use utf8 encoding when downloading results as CSV 2020-08-21 18:35:27 -05:00
Lance Edgar 1b7612ffb0 Add progress for generating "results as CSV" file to download 2020-08-21 18:28:36 -05:00
Lance Edgar 7d8c57170f Add progress for generating "results as XLSX" file to download 2020-08-21 17:42:01 -05:00
Lance Edgar 32b98ae818 Update changelog 2020-08-21 13:18:08 -05:00
Lance Edgar 7f8271e215 Don't require department by default, for new purchasing batch 2020-08-21 12:28:01 -05:00
Lance Edgar 58362ae858 Add buefy theme support for ordering worksheet 2020-08-20 17:56:19 -05:00
Lance Edgar 7a01cb8873 Allow for unknown/missing "changed by" user for product price history 2020-08-20 17:51:59 -05:00
Lance Edgar 374f20ff1a Remove <section> tag around "no results" for minimal b-table 2020-08-20 17:51:21 -05:00
Lance Edgar 9620fc5a83 Add config for "global" help URL 2020-08-20 17:51:00 -05:00
Lance Edgar cfa9c95814 Tweaks for export views, to make more generic 2020-08-19 17:23:23 -05:00
Lance Edgar 96185d17bd Update changelog 2020-08-17 21:56:09 -05:00
Lance Edgar b5028ab2d0 Add pagination to price, cost history grids for product view 2020-08-17 21:38:12 -05:00
Lance Edgar a038f2a98d Make "download row results" a bit more generic
to handle non-native table/rows, w/ non-uuid key
2020-08-16 16:57:06 -05:00
Lance Edgar 7924502b65 Update changelog 2020-08-13 12:55:17 -05:00
Lance Edgar aac0e7d35c Tweak config methods for customer master view 2020-08-11 18:28:03 -05:00
Lance Edgar dca890f169 Update changelog 2020-08-10 19:37:29 -05:00
Lance Edgar d0e7f7dda2 Improve rendering of true_margin column for pricing batch row grid 2020-08-09 15:50:25 -05:00
Lance Edgar b4ea1489a7 Update changelog 2020-08-09 15:06:41 -05:00
Lance Edgar ca31af196f Expose user reference(s) for employees 2020-08-09 14:39:31 -05:00
Lance Edgar 163134326a Coalesce on User.active when merging 2020-08-09 14:32:16 -05:00
Lance Edgar 4371574403 Add model to global template context, plus h.maxlen()
sometimes it's nice to just add a `maxlength="100"` or whatever to an input tag
within some random template.  that should "just be possible" with no extra
effort
2020-08-09 14:03:28 -05:00
Lance Edgar 7d158e58b5 Add protected_usernames() config function 2020-08-06 02:04:17 -05:00
Lance Edgar 808e737202 Add basic/unfinished "new customer order" page/feature
so far creates the order batch, and can set some customer info
2020-08-02 20:59:16 -05:00
Lance Edgar c32f47ba95 Tweak the buefy autocomplete component a bit
to better support staying in sync w/ data on the caller/parent side
2020-08-02 19:13:40 -05:00
Lance Edgar 493785591c Add basic web views for "new customer order" batches 2020-08-02 15:27:10 -05:00
Lance Edgar 9a2a6bbc9f Fix missing scrollbar when version diff table is too wide for screen
at least, this seems to fix.  not sure if/why we shouldn't apply this style
globally always, but playing it safe for now
2020-08-01 22:18:54 -05:00
Lance Edgar 6bd049e0bb Update changelog 2020-07-30 16:39:44 -05:00
Lance Edgar 8ea379bbff Add more customization hooks for making grid actions in master view 2020-07-30 16:38:03 -05:00
Lance Edgar dfeb14e7a8 Update changelog 2020-07-29 21:59:49 -05:00
Lance Edgar cf8072e402 Use handler do_delete() method when deleting a batch
even though it seems we have 2 calls to `session.delete(batch)` now, but things
are still working..fingers crossed
2020-07-29 21:58:31 -05:00
Lance Edgar e0ce7e8505 Add self.cloning convenience indicator for master view 2020-07-28 21:19:47 -05:00
Lance Edgar d196044d11 Update changelog 2020-07-26 14:02:28 -05:00
Lance Edgar 0798102ba5 Tweak "coalesce" logic for merging field data 2020-07-22 19:53:35 -05:00
Lance Edgar 4c3112b85b Fix another pagination bug with buefy grid tables
hopefully this gets it all working right...ugh
2020-07-19 18:43:31 -05:00
Lance Edgar 925e5e0731 Fix permissions wiget bug when creating new role 2020-07-16 19:43:33 -05:00
Lance Edgar 3819dd9469 Fix pagination sync issue with buefy grid tables 2020-07-15 22:05:57 -05:00
Lance Edgar 0dfe52a42d Don't allow "execute results" for any batches by default
custom app must always explicitly opt-in to that feature
2020-07-07 19:23:52 -05:00
Lance Edgar ca64d52021 Make field list explicit for Store views 2020-07-05 00:21:00 -05:00
Lance Edgar 793d80f092 Make field list explicit for Department views 2020-07-04 11:44:09 -05:00
Lance Edgar 4f2f192783 Revert "Freeze version for 'Chameleon' dependency"
This reverts commit aac9bad7ec.

all should be good now, per new 'deform' release
2020-07-03 19:14:30 -05:00
Lance Edgar 6577b3752f Avoid latest SQLAlchemy-Utils when running tests for python2.7 2020-07-02 12:42:42 -05:00
Lance Edgar 66bf11e893 Tweak field label for Product.item_id 2020-06-29 16:57:05 -05:00
Lance Edgar aac9bad7ec Freeze version for 'Chameleon' dependency
pending the fix, which should come w/ next 'deform' release
2020-06-29 13:07:49 -05:00
Lance Edgar bea671987c Update changelog 2020-06-24 12:07:46 -05:00
Lance Edgar e943a1cd44 Convert mako directories to list, if it's a string
so we can push a new path to it, for sake of theme
2020-06-24 11:36:58 -05:00
Lance Edgar c1a2bb978c Use byte string filters for row grid too
if master view needs them at all, chances are they should apply to row grid as
well as main grid
2020-06-24 10:53:43 -05:00
Lance Edgar c7c3dea6b2 Improve support for composite key in master view 2020-06-22 18:26:43 -05:00
Lance Edgar bb11263bad Tweak how we freeze column for role/perm matrix 2020-06-22 16:21:45 -05:00
Lance Edgar e5f0831369 Add ability to download roles / permissions matrix as Excel file 2020-06-22 16:00:33 -05:00
Lance Edgar 6463df7224 Add dropdown, autohide magic when editing Role permissions
only for Buefy theme though
2020-06-22 14:59:17 -05:00
Lance Edgar dc81e5b5c5 Update changelog 2020-06-17 12:45:00 -05:00
Lance Edgar 31df41283c Don't allow edit/delete of rows, if master view says so
also fix "back to parent" link when viewing row
2020-06-12 18:40:10 -05:00
Lance Edgar abea50427e Update changelog 2020-05-27 15:25:52 -05:00
Lance Edgar a8a79ee326 Let each grid component have a custom name, if needed 2020-05-20 19:19:06 -05:00
Lance Edgar 8683e2a4c2 Cap version for 'cornice' dependency
their 5.0 release drops support for python 2.x but we can't do that yet
2020-05-20 16:15:05 -05:00
Lance Edgar 3bb0c8468b Update changelog 2020-05-20 15:53:49 -05:00
Lance Edgar b5f9c8e358 Sort available reports by name, if handler doesn't specify
also add basic support for "decimal" params
2020-05-19 12:42:07 -05:00
Lance Edgar 2139fea3d0 Expose "shelved" field for pricing batches 2020-05-18 14:42:02 -05:00
Lance Edgar b13cae11fa Update changelog 2020-05-15 10:56:56 -05:00
Lance Edgar 2ac2a98727 Don't auto-include "Guest" role when finding roles w/ permission X
otherwise "all" roles are returned when checking for a perm which Guest role
does have granted
2020-05-14 22:07:34 -05:00
Lance Edgar 5f2dd31485 Parse pip requirements file ourselves, instead of using their internals
that problem just kept getting worse, so i stole this solution partly from:

77879cf341
2020-05-14 21:53:41 -05:00
Lance Edgar f0224144b7 Update changelog 2020-04-07 21:19:48 -05:00
Lance Edgar 3a6ced388a Allow the home page to include quickie search
make it easier for any "non-master" view to do so
2020-04-07 13:44:13 -05:00
Lance Edgar 4c3b189108 Update changelog 2020-04-06 13:20:44 -05:00
Lance Edgar cc96d9877b Defer fetching price, cost history when viewing product details
user can ask for that history if they need it, but it's too expensive to always
fetch by default for initial page load
2020-04-06 13:12:38 -05:00
Lance Edgar f2b5e2302a Delete some unwanted tests; delay import for tempmon session
view config can now depend on rattail config, and tests don't like that... but
they didn't really do anything that useful anyway i think
2020-04-04 21:44:01 -05:00
Lance Edgar d9f6a7201e Let config totally disable the old/legacy jQuery mobile app 2020-04-04 20:51:49 -05:00
Lance Edgar d2c4791611 Add basic dashboard page for TempMon
only the older jQuery theme is supported for now...
2020-04-04 19:47:28 -05:00
Lance Edgar 0fbc8c9247 Add initial API views for inventory batches 2020-03-29 16:31:16 -05:00
Lance Edgar e9fc9ccbf7 Use "quick entry" logic from batch handler, for mobile inventory
pretty sure desktop version still needs cleanup, but later...
2020-03-29 15:20:34 -05:00
Lance Edgar 71a9010579 Make handler responsible for locating product for inventory batch 2020-03-29 15:20:34 -05:00
Lance Edgar 0e7835e2d9 Make inventory batch handler responsible for finding row for product 2020-03-29 15:20:32 -05:00
Lance Edgar 069eac1cf6 Add temporary method for inventory batch view
calling code should invoke handler directly instead of using this method, but
for now we need it to exist
2020-03-29 14:30:48 -05:00
Lance Edgar dc4531f545 Let inventory batch handler decide which count modes are available 2020-03-29 13:37:50 -05:00
Lance Edgar 6a58f5f5d3 Let inventory batch handler decide if products should be aggregated 2020-03-29 13:33:38 -05:00
Lance Edgar 12b567d3d2 Let inventory batch handler decide what to do about unknown product scan 2020-03-29 13:09:14 -05:00
Lance Edgar 2532fcbea2 Let inventory batch handler decide if case input is allowed 2020-03-29 13:04:11 -05:00
Lance Edgar 0704717ec5 Let inventory batch handler declare which count modes are allowed
preparing for API/mobile usage
2020-03-29 12:46:41 -05:00
Lance Edgar 242e14e8a9 Allow bulk-delete for Inventory Batches 2020-03-29 12:07:42 -05:00
Lance Edgar 35bef2c3dd Move inventory batch view to its proper location
but keep "inventory adjustment reasons" where it was; that also is proper
2020-03-29 12:05:05 -05:00
Lance Edgar 65f41480eb Allow bulk-delete, merge for Brands table 2020-03-27 18:15:33 -05:00
Lance Edgar aaabde5c5a Add link to generate new report, when viewing one 2020-03-26 15:30:14 -05:00
Lance Edgar 89ffbd6efc Add support for "choice" widget, for report params
also add support for default value, for a param
2020-03-26 15:24:16 -05:00
Lance Edgar 2a4832f9b9 Declare the v-model for "dynamic select" widget 2020-03-24 18:19:25 -05:00
Lance Edgar c14ecd2948 Add helper function, get_csrf_token() 2020-03-24 18:19:05 -05:00
Lance Edgar febe651e31 Stop raising an error if view doesn't define row grid columns
just show whatever is gonna show by default; they can edit list if they want
2020-03-23 22:35:24 -05:00
Lance Edgar af07b433ad Fix rendering of batch ID in forms 2020-03-23 21:41:44 -05:00
Lance Edgar 13802c49a8 Add "generic" render_id_str() method to MasterView
not sure how useful, but maybe
2020-03-23 21:25:43 -05:00
Lance Edgar eaeda6ca36 Fix row status filter for Import/Export batches
per Buefy theme
2020-03-23 20:55:46 -05:00
Lance Edgar af4be59fe0 Add "local only" column to Users grid
but only show if user has perm of course
2020-03-23 20:24:03 -05:00
Lance Edgar 917d5ab3fa Expose the Role.notes field for view/edit
also add a simple "<pre> with sans-serif font" renderer
2020-03-23 19:59:28 -05:00
Lance Edgar cd019fb05b Fix the "change password" form per Buefy theme 2020-03-23 19:33:56 -05:00
Lance Edgar e04e67774e Add common permission for sending user feedback
there can be valid reasons to *not* expose that, so let admin decide
2020-03-23 19:33:00 -05:00
Lance Edgar 51e1a85f0b Fix some spacing in header for Buefy theme 2020-03-22 16:42:05 -05:00
Lance Edgar 297ca3fe11 Fix default row grid config logic for batches
make sure we don't overwrite configured row labels
2020-03-20 14:58:29 -05:00
Lance Edgar 1570871884 Use proper cornice service registration, for API batch execute etc. 2020-03-20 14:40:27 -05:00
Lance Edgar a721ec4a43 Misc. API improvements for sake of mobile receiving 2020-03-20 13:51:34 -05:00
Lance Edgar ad9c193061 Clean up some purchasing views 2020-03-19 14:36:43 -05:00
Lance Edgar 3223a77cb1 Add "danger" style for "delete" grid row action 2020-03-19 00:02:27 -05:00
Lance Edgar e57010cd3d Update changelog 2020-03-18 23:44:34 -05:00
Lance Edgar 0ea4b98b1f Expose more Member data, relationships with Customer, Person 2020-03-18 13:15:11 -05:00
Lance Edgar eb57ebe62b Show member number by default instead of ID
for now..  should probably make configurable though
2020-03-18 12:45:29 -05:00
Lance Edgar 72796d1e04 Expose new Member.number field 2020-03-18 12:29:18 -05:00
Lance Edgar 970b5871e5 Add/improve various display of Member data 2020-03-18 11:27:58 -05:00
Lance Edgar 8ac0bb2334 Expose default email address, phone number when editing a Person 2020-03-17 18:50:07 -05:00
Lance Edgar ff3e83b1c5 Fix name display bug in profile view 2020-03-17 12:58:36 -05:00
Lance Edgar 60157abd46 Allow customization for Customers tab of Profile view
more tabs to come, this was all i needed for now
2020-03-17 12:38:49 -05:00
Lance Edgar 907a356bea Add support for "bulk-delete" of Person table 2020-03-16 17:47:06 -05:00
Lance Edgar 7994c7d770 Expose Customer.number field 2020-03-15 19:28:11 -05:00
Lance Edgar 4fe885995f Allow "touch" for Department, Subdepartment 2020-03-15 15:52:10 -05:00
Lance Edgar da4f2b2081 Add sort/filter for Department Name, in Subdepartments grid 2020-03-15 14:26:56 -05:00
Lance Edgar 9b00e829b8 Prevent deletion of department which still has products 2020-03-15 13:01:52 -05:00
Lance Edgar 964671fcbf Don't let user delete roles to which they belong, without permission 2020-03-15 11:59:39 -05:00
Lance Edgar edd48ef667 Misc. changes to User, Role permissions and management thereof
* only "root" can edit the Administrator role
* edit of Authenticated and Guest roles requires dedicated permission
* edit of role(s) to which current user belongs, requires dedicated permission
* delete is not allowed for any built-in role
* when editing a role, user can only add/remove permissions they themselves have
* settings can define some "protected" users, which only "root" can edit/delete
2020-03-15 11:39:52 -05:00
Lance Edgar 413e9b0f1e Remove old/unwanted Vue.js index experiment, for Users table 2020-03-15 09:40:11 -05:00
Lance Edgar 59cae7d207 Only show tables for "public" schema
i.e. avoid the "batch" schema
2020-03-15 09:26:38 -05:00
Lance Edgar 9a61f55f76 Tweak GPC grid filter, to better handle spaces in user input
i.e. when a user copy/pastes a UPC with leading/trailing space
2020-03-14 18:58:06 -05:00
Lance Edgar 136d181363 Add basic "ordering worksheet" API
display-only for the moment, pending review/feedback
2020-03-13 13:15:27 -05:00
Lance Edgar d8b9ae9ff1 Update changelog 2020-03-11 13:31:59 -05:00
Lance Edgar d72f61a98d Make sure all contact info is "touched" when touching person record 2020-03-11 13:30:04 -05:00
Lance Edgar 12b0ac1037 Move logic for Order Form worksheet into purchase batch handler
i.e. get it out of Tailbone!
2020-03-06 19:53:03 -06:00
Lance Edgar 1db6d642e7 Refactor "view profile" page per latest Buefy theme conventions 2020-03-06 14:01:10 -06:00
Lance Edgar cd0703ba12 Update changelog 2020-03-05 13:03:59 -06:00
Lance Edgar 0f5999c8d8 Allow "touch" for vendor records 2020-03-04 12:59:11 -06:00
Lance Edgar 11cc9a752a Remove "api." prefix for default route names, in API master views 2020-03-03 17:10:41 -06:00
Lance Edgar 0483f47b26 Add support for refreshing multiple batches (results) at once 2020-03-02 18:11:13 -06:00
Lance Edgar 2605f5ab79 Fix batch row status breakdown for Buefy themes
also, fix the "import batch from file" feature UI, per Buefy theme
2020-03-02 14:38:06 -06:00
Lance Edgar 2100f0461d Update changelog 2020-03-02 11:53:15 -06:00
Lance Edgar 0e46b25f6e Use Cornice when registering all "service" API views
pretty sure we'll get *something* for "free" if we do it their way
2020-03-01 17:31:54 -06:00
Lance Edgar c55830e533 Refactor all API views thus far, to use new v2 master 2020-03-01 17:31:54 -06:00
Lance Edgar 113c0af49d Add new "master" API view class; refactor products and batches to use it 2020-03-01 17:31:51 -06:00
Lance Edgar df00dd600a Update changelog 2020-03-01 12:24:41 -06:00
Lance Edgar 86617e410f Fix some basic product editing features
mostly for sake of online demo
2020-02-28 18:11:54 -06:00
Lance Edgar 815cdbdd0a Fix product price, cost history dialogs, for Buefy theme 2020-02-28 17:06:30 -06:00
Lance Edgar a2277feb10 Cleanup main version history views for Buefy theme 2020-02-28 15:45:27 -06:00
Lance Edgar 6d929dd95a Fix how we fetch employee history, for profile view 2020-02-28 13:10:25 -06:00
Lance Edgar 7b43164831 Add support for executing ordering batches via API 2020-02-26 21:29:59 -06:00
Lance Edgar c145d077cd Return employee_uuid along with user info, from API
occasionally that is useful
2020-02-26 21:29:37 -06:00
Lance Edgar a79bf3f055 Add toggle complete, more normalized row fields for odering batch API 2020-02-26 17:45:19 -06:00
Lance Edgar 77eead761e Update changelog 2020-02-26 15:04:56 -06:00
Lance Edgar cd8d70de0e Send batch params as part of normalized API 2020-02-26 14:27:17 -06:00
Lance Edgar 5f8dc20312 Raise 404 not found instead of error, when user is not employee
i.e. when they try to view "employee schedule" or "time sheet"
2020-02-25 15:35:39 -06:00
Lance Edgar 2b70ed1407 Fix "edit row" logic for ordering batch
previous logic allowed `colander.null` to be passed to batch handler, which
caused an error.  also it allowed editing "all" fields for the row, which we
really don't need to do, so now we just support the order quantities
2020-02-24 13:38:58 -06:00
Lance Edgar fc830f60e8 Tweak worksheet_update() of ordering batch view, to leverage handler
specifically this is to make use of handler's `update_row_quantity()` method,
when user enters new order quantities via worksheet
2020-02-24 12:36:47 -06:00
Lance Edgar c3f4a3d9ea Tweak save_edit_row_form() of purchase batch view, to leverage handler
specifically this is to make use of handler's `update_row_quantity()` method,
when editing a row for ordering batches
2020-02-24 12:27:26 -06:00
Lance Edgar 6c5cc95e51 Overhaul the /ordering batch API somewhat; update docs
mostly a savepoint; the /ordering API still needs some work for sure
2020-02-23 21:07:50 -06:00
Lance Edgar 877e6088e2 Update changelog 2020-02-21 14:30:08 -06:00
Lance Edgar c96ab426a4 Return new user permissions when logging in via API 2020-02-21 12:36:11 -06:00
Lance Edgar 5e028ce547 Add API view for changing current user password 2020-02-12 17:32:18 -06:00
Lance Edgar da16f25cf2 Update changelog 2020-02-12 14:49:32 -06:00
Lance Edgar c9cf59762a Return package names as list, from "about" page from API
so client knows in what order to display package versions
2020-02-12 14:47:48 -06:00
Lance Edgar c95008703c Add common get_user_info() method for all API views 2020-02-11 13:31:02 -06:00
Lance Edgar a6f80e07e0 Add way to prevent user login via API, per custom logic 2020-02-10 15:43:10 -06:00
Lance Edgar 5faced8d22 Tweak how default config is defined for auth API views
so it may be more easily extended
2020-02-10 14:13:50 -06:00
Lance Edgar 4a35620820 Allow override of "email key" for user feedback, sent via API 2020-02-10 12:35:30 -06:00
Lance Edgar 76839c48cf Fix email preview for TXT templates on python3 2020-02-09 15:32:22 -06:00
Lance Edgar 6925c460c5 Add some custom display logic for "current price" in pricing batch 2020-02-07 18:12:44 -06:00
Lance Edgar 6a8f64a9e8 Use new Email.obtain_sample_data() method when generating preview
per upstream changes
2020-02-07 16:21:51 -06:00
Lance Edgar f1dc773bfd Update changelog 2020-02-03 18:46:19 -06:00
Lance Edgar d00449465f Go ahead and expose theme picker by default
might as well let everyone see that out of the gate..right?
2020-01-29 22:20:53 -06:00
Lance Edgar 77f26f01d4 Make sure falafel theme is somewhat available by default 2020-01-29 22:01:44 -06:00
Lance Edgar b633c91b66 Add red highlight for SRP breach, for generic product batch 2020-01-28 17:24:10 -06:00
Lance Edgar 132b2b9ec7 Fix vendor ID/name for Excel download of pricing batch rows 2020-01-28 16:33:23 -06:00
Lance Edgar b875540397 Update changelog 2020-01-28 15:11:31 -06:00
Lance Edgar 201f7cc21e Add warning for "price breaches SRP" rows in pricing batch 2020-01-28 11:59:40 -06:00
Lance Edgar 6e7ee99b47 Sort report options by name, when choosing which to generate 2020-01-28 06:47:59 -06:00
Lance Edgar 99f1e000bf Stop including deprecated views
probably this only affected the "tests"
2020-01-27 16:13:28 -06:00
Lance Edgar 35875b7826 Tweak how we import pip internal things, for upgrade view
ugh, just kicking the can down the road here
2020-01-27 12:57:40 -06:00
Lance Edgar e9533727db Allow populate of new pricing batch from products w/ "SRP breach" 2020-01-23 10:48:21 -06:00
Lance Edgar 842882e766 Include regular price changes, for current price history dialog 2020-01-21 11:41:37 -06:00
Lance Edgar 09e18b064d Update changelog 2020-01-20 12:28:49 -06:00
Lance Edgar 0e4b33be96 Add "cost history" dialog for product view
older jquery theme only, for now
2020-01-16 11:56:45 -06:00
Lance Edgar 91c1c1c5c8 Add "current price history" dialog for product view
hopefully this does everything it needs to...guess we'll see
2020-01-16 11:31:49 -06:00
Lance Edgar 09a383f89c Fix SRP warning logic!
dang, had it reversed for some testing and then forgot to switch back
2020-01-15 19:26:28 -06:00
Lance Edgar 133ca622a0 Expose batch ID, sequence for datasync change queue 2020-01-15 19:03:16 -06:00
Lance Edgar 0fbe3380cd Highlight SRP in red, if reg price is greater (in product view) 2020-01-14 16:49:56 -06:00
Lance Edgar 8f07f27a61 Highlight SRP in red, if reg price is greater (in products grid)
seems like a good enough idea generally...
2020-01-14 16:35:30 -06:00
Lance Edgar bbd462c85a Cleanup "diff" table for importer batch row view, per Buefy theme 2020-01-14 12:15:02 -06:00
Lance Edgar 234fd8b2e1 Add support for Row Status Breakdown, for Import/Export batches 2020-01-14 11:54:00 -06:00
Lance Edgar 02649709aa Add regular price history dialog for product view 2020-01-08 08:04:48 -06:00
Lance Edgar 910e82a795 Hide the SRP history link for new buefy themes
until support for that is added...
2020-01-07 06:44:27 -06:00
Lance Edgar 3fc8254219 Update changelog 2020-01-06 08:03:29 -06:00
Lance Edgar 4c5b01f287 Move "delete results" logic for master grid
should be easier to customize this way..?  previous way seemed to be broken
2020-01-06 07:46:10 -06:00
Lance Edgar 03c8d3409a Update changelog 2020-01-02 12:39:32 -06:00
Lance Edgar 7dce154cc3 Add dialog for viewing product SRP history
only old jquery theme is supported, for now
2020-01-02 06:55:02 -06:00
Lance Edgar 8947a4d14f Add Grid.set_filters_sequence() convenience method
sometimes a properly-ordered filter sequence can really help
2020-01-01 12:05:08 -06:00
Lance Edgar 3895734c32 Update changelog 2019-12-04 16:53:39 -06:00
Lance Edgar a96c5712ab Use currency formatting for costs in vendor catalog batch
and related tweaks
2019-12-04 16:51:55 -06:00
Lance Edgar 7b5ac7eba4 Update changelog 2019-12-02 08:40:45 -06:00
Lance Edgar 6c029382d9 Add API views for admin user to become / stop being "root" 2019-11-26 16:42:27 -06:00
Lance Edgar 31ae68f96e Allow override of user authentication logic for API 2019-11-26 15:28:03 -06:00
Lance Edgar 8cbabfbb95 Add API view for marking "receiving complete" for receiving batch 2019-11-26 13:54:43 -06:00
Lance Edgar 675660e130 Expose catalog cost, allow updating, for receiving batch rows 2019-11-26 11:19:55 -06:00
Lance Edgar 3e1409afc5 Show vendor item code in receiving batch row grid 2019-11-22 20:12:51 -06:00
Lance Edgar c14cf3022c Allow update of row unit cost directly from receiving batch view 2019-11-22 20:12:48 -06:00
Lance Edgar b7c710cddd Update changelog 2019-11-19 13:48:12 -06:00
Lance Edgar bed9ad76f9 Filter by receiving mode, for receiving batch API 2019-11-19 13:19:37 -06:00
Lance Edgar d9fecd8eb5 Update changelog 2019-11-15 16:26:22 -06:00
Lance Edgar d256e2014a Provide background color when first checking API session 2019-11-15 16:16:59 -06:00
Lance Edgar 0715bd6321 Add basic "receive" handler logic for receiving API 2019-11-15 16:16:56 -06:00
Lance Edgar 337422a619 Tweak some "unexpected item" logic for receiving API 2019-11-15 10:30:01 -06:00
Lance Edgar 6a98dcc169 Add toggle complete; improve quick entry for receiving batch API 2019-11-13 14:05:38 -06:00
Lance Edgar d42c2fabb9 Move "quick entry" logic for purchase batch, into rattail handler 2019-11-13 14:04:11 -06:00
Lance Edgar a096ce565e Add some convenience filters for receiving batch rows API 2019-11-12 19:04:46 -06:00
Lance Edgar a9b740dcaa Add basic support for "eligible purchases" for receiving batch API 2019-11-12 17:46:18 -06:00
Lance Edgar 3514c4050e Add some API views for receiving, and vendor autocomplete
lots more to do yet, for those...
2019-11-12 11:55:28 -06:00
Lance Edgar afdd294c60 Add support for "toggle complete" for batch API 2019-11-11 12:36:50 -06:00
Lance Edgar bd09acd0fd Add support for label batch "quick entry" API
plus other general improvements to API core/master views and config
2019-11-11 11:26:42 -06:00
Lance Edgar c520dc23ba Update changelog 2019-11-08 16:16:43 -06:00
Lance Edgar c70dedd94f Fix merge feature for master index grid
at least, for Vue.js / buefy theme
2019-11-08 16:02:21 -06:00
Lance Edgar 0877cfc3c9 Fallback to referrer if form has no cancel button URL 2019-11-07 22:56:56 -06:00
Lance Edgar 8dcec94aec Add notes to label batch API (get), basic create support 2019-11-07 11:02:12 -06:00
Lance Edgar 4afbb350ce Add very basic API views for label batches
still just trying to prove some concepts for now
2019-11-06 17:25:25 -06:00
Lance Edgar 99f69c13d2 Allow rendering of "raw" datetime as ISO date
sometimes a full timestamp isn't that helpful
2019-11-05 15:11:07 -06:00
Lance Edgar 93a44d83d2 Declare empty component list for TailboneForm
easier to add to that if it's already there
2019-11-04 20:55:51 -06:00
Lance Edgar 86695c9dc7 Refactor "send new message" form, esp. recipients field, per Vue.js 2019-11-04 19:06:58 -06:00
Lance Edgar e153e530a8 Use "warning" status for pricing batch rows, where product not found 2019-11-04 12:39:26 -06:00
Lance Edgar e99f225def Add some padding above/below form fields 2019-11-04 12:12:08 -06:00
Lance Edgar 7f94e3fc77 Tweak a method signature for batch views
sometimes executing a batch will involve an "action" kwarg, so we can't use
that or else a collision ensues
2019-11-01 21:09:15 -05:00
Lance Edgar 8af3d53a3c Improve/fix some views for Messages per Vue.js theme 2019-11-01 15:58:56 -05:00
Lance Edgar bcfb4f257d Improve checkbox click handling support for grids
i.e. let custom use define click handlers
2019-10-31 18:03:17 -05:00
Lance Edgar a857d31776 Add Vue.js support for "delete selected" grid feature 2019-10-31 15:02:03 -05:00
Lance Edgar ebc22d845a Add Vue.js support for "enable / disable selected" grid feature 2019-10-31 13:28:00 -05:00
Lance Edgar 847136b69c Refactor "make batch from products query" per Vue.js theme 2019-10-31 12:45:22 -05:00
Lance Edgar 4a35c231f8 Always store CSRF token for each page in Vue.js theme
so child components don't have to "redefine" it.  at least, those that know
about the page already having it...
2019-10-30 20:38:33 -05:00
Lance Edgar 8ff69e8eda Bump default Buefy version to 0.8.2
had previously thought that 0.8.0 broke us somehow, but now 0.8.2 seems fine...
2019-10-30 20:38:06 -05:00
Lance Edgar 2e92f561d8 Assume "local only" flag should be ON by default, for new objects
i.e. if that is a thing, for the given master view
2019-10-29 19:58:08 -05:00
Lance Edgar 0c96062618 Update changelog 2019-10-25 13:27:00 -05:00
Lance Edgar 6536926f3c Don't bug out if can't update roles for user
based on perms, or missing data field
2019-10-24 12:25:59 -05:00
Lance Edgar 39b1a78b89 Allow bulk delete of New Product batch rows 2019-10-23 14:50:36 -05:00
Lance Edgar 15f7018aab Update changelog 2019-10-23 12:44:21 -05:00
Lance Edgar 9606b08c89 Fix JS bug for graph view of tempmon probe readings 2019-10-22 11:07:33 -05:00
Lance Edgar b311c6be7d Add config flag to "force unit item" for inventory batch 2019-10-21 15:35:30 -05:00
Lance Edgar 65bcd8da2a Improve default behavior for clone operation
copy all fields but uuid, and show flash message(s)
2019-10-17 16:17:43 -05:00
Lance Edgar 85e67a974a Update changelog 2019-10-17 15:08:27 -05:00
Lance Edgar 4afe8e900e Don't bug out if stores, departments fields aren't present for Employee 2019-10-15 20:33:37 -05:00
Lance Edgar c525b16581 Fix a label for Employees grid 2019-10-15 19:55:56 -05:00
Lance Edgar 0de34bfec1 Update changelog 2019-10-15 17:30:34 -05:00
Lance Edgar 9fe585bede Fix permissions for add/edit/delete notes from people profile view 2019-10-15 16:12:56 -05:00
Lance Edgar 3b65b06a3d Fix buefy grid pager bug
"has results, then no results" bug where spinner kept going
2019-10-15 15:30:52 -05:00
Lance Edgar fa52ff5545 Update changelog 2019-10-14 15:25:54 -05:00
Lance Edgar c0219938e3 Show active flag for users mini-grid on Role view page 2019-10-14 11:54:01 -05:00
Lance Edgar ec8ce36bd5 Only show action URL if present, for Buefy grid rows 2019-10-12 18:35:22 -05:00
Lance Edgar 6c228a59f2 Use self.has_perm() within MasterView 2019-10-12 18:35:11 -05:00
Lance Edgar 1e0f707a6d Update changelog 2019-10-12 13:41:00 -05:00
Lance Edgar acda689b15 Honor configured db key sequence; let config hide some db keys from UI 2019-10-12 13:34:00 -05:00
Lance Edgar 3dd70926b9 Expose unit cost diff for vendor invoice batch rows 2019-10-09 15:58:16 -05:00
Lance Edgar adf377c41d Move module for vendor invoice batch views to a new home 2019-10-09 15:38:12 -05:00
Lance Edgar 9a35a31261 Expose the "is preferred vendor" flag for vendor catalog batches 2019-10-09 14:55:51 -05:00
Lance Edgar f0a3607fb7 Fix docs, per recent view switcharoo 2019-10-09 14:21:48 -05:00
Lance Edgar b451f4af55 Move module for vendor catalog batch views to a new home
also, expose new "unit cost diff percent" field
2019-10-09 14:16:44 -05:00
Lance Edgar 18c30fcb05 Allow bulk-delete for some common batches 2019-10-09 11:26:26 -05:00
Lance Edgar b14a4987d2 Move label batch views to tailbone.views.batch.labels
to better match the general pattern we have going on
2019-10-09 11:11:22 -05:00
Lance Edgar 700813fa57 Add "is false or null" verb for boolean grid filters 2019-10-09 10:55:13 -05:00
Lance Edgar c812519931 Fix URL for user, for feedback email 2019-10-08 10:08:13 -05:00
Lance Edgar fbeb48a021 Update changelog 2019-10-08 09:39:44 -05:00
Lance Edgar 43f366d955 Improve docs a bit, for GridFilter.set_choices() 2019-10-08 09:38:03 -05:00
Lance Edgar ace18e86ff Fix label bug for grid filter with value choices dropdown 2019-10-08 09:37:21 -05:00
Lance Edgar 08f86a3a7f Update changelog 2019-10-07 11:54:53 -05:00
Lance Edgar 47669a23bc Add support for "local only" Person, User, plus related security
also add "view / edit roles for user" permissions
2019-10-04 22:31:19 -05:00
Lance Edgar 4d1fa4f2d6 Update changelog 2019-10-04 14:32:06 -05:00
Lance Edgar c8e689712a Add forbidden() convenience method to core View class 2019-10-02 18:11:26 -05:00
Lance Edgar 59d3a18b3f Update changelog 2019-10-02 11:08:42 -05:00
Lance Edgar 3199b4ee6c Fix "progress" behavior for upgrade page
per recent changes to progress page, whoops
2019-09-26 21:23:22 -05:00
Lance Edgar fdc687ed45 Update changelog 2019-09-25 11:40:57 -05:00
Lance Edgar ff9700e23a Add core View.make_progress() method
so callers no longer need to import `SessionProgress` and create directly
2019-09-25 00:32:41 -05:00
Lance Edgar f0a5265a65 Update changelog 2019-09-24 14:26:20 -05:00
Lance Edgar 64f81e3396 Show "image not found" placeholder image for products which have none 2019-09-24 11:15:44 -05:00
Lance Edgar c16349d5c3 Fix progress page so it effectively fetches progress data synchronously
i.e. use `setTimeout()` instead of `setInterval()` and only set next timeout
after previous fetch has succeeded
2019-09-20 17:26:33 -05:00
Lance Edgar fe413ba2f5 Honor kwargs used for MasterView.get_index_url() 2019-09-19 20:19:52 -05:00
Lance Edgar 0d2f6e060f Use simple_error() from rattail, for showing some error messages
just trying to standardize a little
2019-09-19 20:19:26 -05:00
Lance Edgar a972fb7359 Add todo comment 2019-09-14 15:07:47 -05:00
Lance Edgar 99cd9f9450 Update changelog 2019-09-09 20:04:08 -05:00
Lance Edgar 1165fa8cdb Show product image from database, if it exists
only use POD image if we have nothing in our DB (and config doesn't say not to)
2019-09-09 19:25:18 -05:00
Lance Edgar ab1ff48527 Update changelog 2019-09-09 18:31:10 -05:00
Lance Edgar 1a6f9c2159 Fix 'about' page template for Buefy themes 2019-09-09 17:34:24 -05:00
Lance Edgar 4c42ccc7d7 Fix various templates for generating reports, per Buefy
also various other tweaks which came up along the way...
2019-09-06 17:45:59 -05:00
Lance Edgar cb4e9e9eda Tweak login form styles, so inputs are same size 2019-09-06 16:43:22 -05:00
Lance Edgar 192c3c201d Prevent text wrap for pricing panel fields on product view page 2019-09-04 16:59:44 -05:00
Lance Edgar 2185182eee Fix rendering of "handheld batches" field for inventory batch view 2019-09-04 10:33:32 -05:00
Lance Edgar 79be69f8c1 Include tax1 thru tax3 flags in form fields for product view page 2019-09-03 13:44:59 -05:00
Lance Edgar c874e879c1 Don't show Delete Row button for executed batch, on jquery mobile site 2019-08-30 20:01:49 -05:00
Lance Edgar ed53bd487b Add basic API endpoints for /ordering-batch 2019-08-30 19:47:30 -05:00
Lance Edgar c41a7303df Add /products API endpoint, enable basic filter support for API views 2019-08-30 19:47:27 -05:00
Lance Edgar c1c37aad85 Cleanup styles for login form 2019-08-30 13:56:06 -05:00
Lance Edgar 7aa5c8e724 Tweak how we return single record data from API
i.e. always return 'data' key with object data, regardless of model
2019-08-30 00:27:03 -05:00
Lance Edgar 56974ce30f Tweak return value for /customers API 2019-08-30 00:27:03 -05:00
Lance Edgar de46dfc4a2 Return current user permissions when session is checked via API 2019-08-30 00:27:00 -05:00
Lance Edgar 47efc88228 Add basic API view for "about" page
i.e. this returns app version and some package versions
2019-08-29 19:06:51 -05:00
Lance Edgar 19c734683b Add basic API view for user feedback 2019-08-29 18:30:13 -05:00
Lance Edgar d97f95fb92 Add basic support for "between" verb, for date range grid filter
this seems to be complete, but we'll see in practice if i forgot something..
2019-08-29 17:23:32 -05:00
Lance Edgar 14778757d9 Expose api.SortColumn
even though we probably shouldn't?  guess we'll see
2019-08-26 00:57:42 -05:00
Lance Edgar 300efe4877 Tweak how we detect JSON request body instead of POST params
i.e. when processing submitted form data
2019-08-25 17:35:01 -05:00
Lance Edgar 3d3ace1c2a Add basic support for create and update actions in API views
customer views only for now, will add more upon further testing
2019-08-25 16:02:59 -05:00
Lance Edgar 7c0d9c4f93 Include short_name in field list returned by /session API 2019-08-24 20:35:56 -05:00
Lance Edgar 9081985b08 Include uuid in fields returned by /customers API 2019-08-24 19:16:41 -05:00
Lance Edgar 5cfe69d24b Hopefully fix "single store" behavior when make a new ordering batch
this does seem to fix for this use case, but previous commit for this code
implied that it was fixing something for the receiving use case...
2019-08-23 22:05:09 -05:00
Lance Edgar 937c2920ac Set default max height, width for app logo
should affect home page and login page
2019-08-23 22:02:46 -05:00
Lance Edgar fd700e06f4 Let a grid have custom ajax data url
i.e. instead of assuming we should GET the current "partial" page, can use a
different URL altogether now
2019-08-23 19:28:36 -05:00
Lance Edgar e6dff16550 Update changelog 2019-08-21 17:49:17 -05:00
Lance Edgar f2c06042cd Tweak login page logo style for jQuery (non-Buefy) themes 2019-08-17 19:01:29 -05:00
Lance Edgar a49d107a82 Provide today's date as context for profile view 2019-08-11 17:30:08 -05:00
Lance Edgar b0af78f3b2 Update changelog 2019-08-05 19:16:13 -05:00
Lance Edgar dade379dcf Fix "last sold" field rendering for product view 2019-08-05 16:06:23 -05:00
Lance Edgar f3ac3ca25e Yet another fix for user feedback form handling 2019-08-04 22:59:32 -05:00
Lance Edgar 243c69b231 Fix some user feedback form handling
sheesh i don't see how this hasn't been more broken for some time now...
2019-08-04 22:36:58 -05:00
Lance Edgar fda7230bce Fix form handling for user feedback
issue was noticed on demo site, but possibly existed elsewhere?  also not 100%
sure about this fix, but it's believed to be okay...
2019-08-04 22:14:15 -05:00
Lance Edgar 287464362e Remove unused "login tips" for demo 2019-08-04 21:52:11 -05:00
Lance Edgar 222e909686 Update changelog 2019-08-04 20:49:11 -05:00
Lance Edgar 1b1d37b9df Fix home and login pages for Buefy theme
not sure what broke those so bad...they're still not "great"
2019-08-04 20:43:31 -05:00
Lance Edgar 5a25ffe6e4 Update changelog 2019-08-04 19:13:27 -05:00
Lance Edgar 6d846ab0db Bring all of header into WholePage component
now there is only *one* Vue.js app instantiated on each page, yay!
2019-08-03 19:20:42 -05:00
Lance Edgar 47c2742878 Move the "HUD" content title section into WholePage component
that way, ThisPage can dynamically trigger a change in the title HTML
2019-08-03 17:56:18 -05:00
Lance Edgar 69eb54abf6 Highlight former Employee records as red/warning 2019-08-03 17:18:53 -05:00
Lance Edgar 1bb0330ab5 Refactory Buefy templates to use WholePage and ThisPage components
plus add `GridFilter.set_choices()` method
2019-08-03 16:57:13 -05:00
Lance Edgar c64fca852c Allow "touch" for Person records 2019-08-02 11:30:46 -05:00
Lance Edgar 01fcd3175f Update changelog 2019-07-31 17:33:38 -05:00
Lance Edgar c04c0e29bb Freeze Buefy version at pre-0.8.0
since apparently their 0.8.0 release breaks some grid filter action
2019-07-31 17:32:57 -05:00
Lance Edgar 9daf1dea31 Update changelog 2019-07-30 19:00:45 -05:00
Lance Edgar 4a175c76f9 Add proper support for composite primary key, in MasterView
at least hopefully this is complete, and didn't break anything else...
2019-07-29 19:27:23 -05:00
Lance Edgar 4d3ff6ed20 Update changelog 2019-07-25 16:53:07 -05:00
Lance Edgar 6e1f925944 Cleanup 'phone' filter/sort logic for Employees grid
per newer conventions etc.  needed to override some of this for a client
2019-07-25 16:05:10 -05:00
Lance Edgar e756ae3c8f Add "multi-engine" support for Trainwreck transaction views 2019-07-25 15:40:38 -05:00
Lance Edgar b07365b487 Add perm for editing employee history from profile view 2019-07-23 13:12:36 -05:00
Lance Edgar f1b6f8a3e4 Add 'disabled' prop for buefy datepicker
also make sure we return `null` when input is empty
2019-07-23 13:12:07 -05:00
Lance Edgar ad3b660bc0 Update changelog 2019-07-13 19:27:05 -05:00
Lance Edgar dd773d4e5e Send URL for viewing employee, along to profile page template 2019-07-11 15:17:25 -05:00
Lance Edgar 61df7745c6 Use latest version of Buefy by default, for falafel theme 2019-07-11 15:17:04 -05:00
Lance Edgar aeaef04fac Add convenience method for gathering employee history context data
so we can reuse that for returning JSON from various views
2019-07-11 14:01:22 -05:00
Lance Edgar 8c2287a1e8 Add custom permissions for People "profile" view
this whole thing needs some polishing yet...
2019-07-11 14:01:00 -05:00
Lance Edgar fa825da404 Include employee history data in context for "view profile" 2019-07-10 22:58:05 -05:00
Lance Edgar 839f6affe2 Add basic "DB picker" support, for views which allow multiple engines
i.e. whichever engine is "current" will determine where data comes from
2019-07-09 22:14:12 -05:00
Lance Edgar 0d7492f6be Update changelog 2019-07-09 15:18:31 -05:00
Lance Edgar c09437880f Add support for general "view click handler" for <b-table> element
plus some other tweaks for sake of revision history in profile view
2019-07-09 14:56:33 -05:00
Lance Edgar 069ccab0ae Clear feedback message after sending
that way user can open dialog again, and things not be weird
2019-07-05 19:54:57 -05:00
Lance Edgar b8274d92db Refactor feedback dialog for Buefy themes
for more proper Vue.js component usage pattern
2019-07-05 19:50:16 -05:00
Lance Edgar 4499a872d8 Remove unwanted "export has file" logic for ExportMasterView
this mostly did what "downloadable" already did, plus some other stuff which it
probably shouldn't have been doing anyway
2019-07-05 18:17:53 -05:00
Lance Edgar 94d7e01bd5 Add download_path() method for ExportMasterView
default behavior isn't very smart, subclass should override as needed
2019-07-05 18:00:58 -05:00
Lance Edgar 993ce9289d Add basic "downloadable" support for ExportMasterView
instead of it trying to do its own thing for that...  more to come on this
2019-07-05 16:48:29 -05:00
Lance Edgar c8d6361c36 When creating an export, set creator to current user
many exports won't support creation via web app, but some will
2019-07-05 16:47:51 -05:00
Lance Edgar 8c610e2142 Add render_customer() method for MasterView
surely will be commonly useful?
2019-07-05 16:44:46 -05:00
Lance Edgar bb0e2fb9e9 Add way to hide "view profile" helper for customer view 2019-07-03 12:35:14 -05:00
Lance Edgar e9e4d65c78 Update changelog 2019-07-01 15:18:06 -05:00
Lance Edgar ddc8bd2028 Fix product view template per Buefy refactoring 2019-07-01 14:08:42 -05:00
Lance Edgar bf9fff6065 Update changelog 2019-07-01 13:23:27 -05:00
Lance Edgar 744347c269 Clear checked rows when refreshing async grid data
we don't want to accidentally "remember" checked rows which aren't currently
visible...  at least not yet / by default
2019-07-01 13:22:38 -05:00
Lance Edgar d087071fc9 Update changelog 2019-07-01 12:27:38 -05:00
Lance Edgar a4d6c6694a Make sure grid action links preserve white-space
i.e. don't wrap between link icon and text!
2019-07-01 11:46:52 -05:00
Lance Edgar ff3ee351d1 Add 'duration_hours' type for grid column display 2019-07-01 11:46:21 -05:00
Lance Edgar b14e8daa1a Expose a way to embed "raw" data values within Buefy grid data
for sake of custom front-end stuff
2019-06-28 15:47:52 -05:00
Lance Edgar 3a53ffcc23 Add NumericInputWidget for use with Buefy themes
uses a Vue.js component for better logic encapsulation
2019-06-28 13:06:43 -05:00
Lance Edgar 2abe589ef6 Allow "touch" for customer records 2019-06-27 19:50:24 -05:00
Lance Edgar f81e4fac79 Update changelog 2019-06-25 20:49:56 -05:00
Lance Edgar a4b27115ac Refactor all Buefy form submit buttons, per Chrome behavior
ugh, what a pain.  and turns out i'd previously ran into this same issue for
jQuery, per commit e945ebe325
2019-06-25 20:32:49 -05:00
Lance Edgar 43a210cac4 Fix "edit row" icon for batch row grids, for Buefy themes 2019-06-21 15:47:30 -05:00
Lance Edgar 355a49e463 Fix PO total calculation bug for mobile ordering
also fix currency formatting for PO calculated total
2019-06-21 15:18:41 -05:00
Lance Edgar 975aa0a3cc Only expose "Make User" button when viewing a person
i.e. don't expose when editing the person
2019-06-18 16:54:05 -05:00
Lance Edgar a99b8c6aaf Update changelog 2019-06-18 16:49:55 -05:00
Lance Edgar 8a968a1f89 Fix inheritance issue with "view row" master template 2019-06-18 16:48:55 -05:00
Lance Edgar c7eeaffec9 Update changelog 2019-06-18 16:25:37 -05:00
Lance Edgar cc79fe76fd Refactor form/page component structure for Buefy/Vue.js
this also moves Execute Batch from the form buttons area, to object helper
2019-06-17 15:07:19 -05:00
Lance Edgar 4cadeb8e5d Fix click behavior for all/diffs package links in upgrade view 2019-06-16 16:27:45 -05:00
Lance Edgar 76a19ebe5b Pull the grid tools to the right, for Buefy 2019-06-16 16:23:01 -05:00
Lance Edgar b5613ec6dc Only include execute form if applicable, for batch grid view 2019-06-16 16:22:48 -05:00
Lance Edgar 26137ec81e Add Buefy support for "execute results" from core batch grid view 2019-06-16 15:50:40 -05:00
Lance Edgar 0e67c62c86 Add generic /page.mako template
helps with getting a proper Vue.js app going for arbitrary extra pages
2019-06-16 14:43:29 -05:00
Lance Edgar 90bf4edf0d Update changelog 2019-06-16 13:29:35 -05:00
Lance Edgar d51fe8483a Buefy support for "mark batch as (in)complete" 2019-06-15 20:47:45 -05:00
Lance Edgar 3ddde1a1ca Use locale formatting for some numbers in the Buefy grid 2019-06-15 19:50:25 -05:00
Lance Edgar 48e28a1ba4 Assign client IP address to session, for sake of data versioning 2019-06-15 19:02:51 -05:00
Lance Edgar 558e127caa Fix package diff table for upgrade view template, per Buefy 2019-06-15 18:06:54 -05:00
Lance Edgar 63807e71fd Use once-button for tempmon client restart 2019-06-15 17:31:49 -05:00
Lance Edgar f684c38958 Refactor tempmon probe graph view per Vue.js 2019-06-15 17:00:46 -05:00
Lance Edgar 4b2abf791c Refactor tempmon probe view template, per Buefy 2019-06-15 15:51:25 -05:00
Lance Edgar a8b83d9fe1 Update changelog 2019-06-14 21:33:29 -05:00
Lance Edgar 4ce695d933 Make person, created by fields readonly when editing Person Note 2019-06-14 20:47:16 -05:00
Lance Edgar 44aa54f247 Fix some response headers per python 3 2019-06-13 14:25:54 -05:00
Lance Edgar 25e5739b34 Update changelog 2019-06-13 13:50:22 -05:00
Lance Edgar 33e1bd567d Add some vendor fields for product Excel download 2019-06-13 10:00:29 -05:00
Lance Edgar f727c87b56 Add Buefy support for "delete w/ simple confirm" from index grid 2019-06-10 21:59:10 -05:00
Lance Edgar 3775c53df3 Add generic support for "delete w/ simple confirm" in master index template
jquery only, for now
2019-06-09 21:34:47 -05:00
Lance Edgar e715794f04 Add Buefy support for "simple" delete confirmation 2019-06-08 21:02:32 -05:00
Lance Edgar 796170100f Add support for "simple confirm" of object deletion
i.e. can just use `window.confirm()` instead of showing full confirm page

note, this is jquery-only for now
2019-06-08 18:50:16 -05:00
Lance Edgar f25e4fab28 Tweak structure of "view product" page to support Buefy, context menu 2019-06-08 15:50:16 -05:00
Lance Edgar c44c6c79f9 Only tweak field value width for "normal" primary forms, in falafel theme 2019-06-08 15:17:40 -05:00
Lance Edgar 3f6d5daa1e More Buefy tweaks, for file upload, and "edit batch" generally 2019-06-08 14:26:33 -05:00
Lance Edgar 7224be9de2 More Buefy form cleanup for upgrades, clone/execute 2019-06-08 14:06:07 -05:00
Lance Edgar 2b6d88105c Add support for Buefy autocomplete; several other form tweaks
at least the Edit User form should work now, for instance
2019-06-08 13:46:00 -05:00
Lance Edgar d7e19865de Update calculated PO totals for purchasing batch, when editing row 2019-06-08 13:16:57 -05:00
Lance Edgar 643d29ba57 Use <once-button> for "find by perm" feature for Users, Roles 2019-06-06 16:53:16 -05:00
Lance Edgar 4b6038c50c Use <once-button> for app settings form 2019-06-06 16:46:11 -05:00
Lance Edgar f10a80333b Add <once-button> for sending email preview; various other tweaks 2019-06-06 15:58:46 -05:00
Lance Edgar 0a80e01d0b Use <once-button> for restarting datasync daemon 2019-06-06 15:11:01 -05:00
Lance Edgar 93a3da2335 Tweak initial v-bind model value for Buefy form, when is colander.null 2019-06-06 15:10:33 -05:00
Lance Edgar 96c5bd0b69 Fix "current value" for <b-select> element in e.g. edit form views
apparently marking an `<option>` as "selected" does not cut it for Buefy, and
we must bind to a v-model somehow.  not real crazy about the current method,
but it does seem to work okay so far...
2019-06-06 14:57:31 -05:00
Lance Edgar 1ee76878d9 Various things to support "notes management" from person profile view 2019-06-06 13:49:59 -05:00
Lance Edgar 6749604210 Add a generic "user" field renderer to master view
and use it for PersonNote view
2019-06-05 16:04:14 -05:00
Lance Edgar e097f526bb Turn on bulk-delete feature for Raw Settings view
this can be dangerous, but then that's why we have a permissions system
2019-06-04 23:08:21 -05:00
Lance Edgar ea0aff1a3e Tweak permissions styles for view/edit of User, Role
per Buefy themes, but still compatible with jQuery theme also
2019-06-04 19:53:47 -05:00
Lance Edgar 40ab3cda9c Initial support for adding new PersonNote from profile view 2019-06-04 19:23:27 -05:00
Lance Edgar c24098a117 Fall back to parsing request body as JSON for form data
apparently that's what we'll be dealing with from Vue.js AJAX requests?
2019-06-04 17:57:48 -05:00
Lance Edgar 5c28f10921 Improve props handling for <once-button> component
now we use computed properties for some of the underlying button props.  this
also adds a "click" event for the element; callers should be able to add
handler for that which happens *in addition to* the button disabling.  it's
assumed that's always safe or else caller wouldn't use `<once-button>`
2019-06-04 17:50:14 -05:00
Lance Edgar 1c07508f39 Add <b-table> element template for simple grids with "static" data 2019-06-04 13:33:56 -05:00
Lance Edgar e5472a6fae Add json_response() convenience method for all views
er, class-based views anyway
2019-06-04 12:15:32 -05:00
Lance Edgar e06f8c16df Tweak styles for context menu on falafel theme 2019-06-03 16:39:17 -05:00
Lance Edgar a1d7059c0b Update changelog 2019-06-03 15:01:13 -05:00
Lance Edgar bbe2efa4b3 Allow bulk row delete for generic products batch 2019-05-30 13:25:42 -05:00
Lance Edgar 1fb121fb6d Add Buefy panels support for "view product" page 2019-05-23 19:15:47 -05:00
Lance Edgar 6be4964221 Use <once-button> where applicable for CRUD forms 2019-05-23 18:13:19 -05:00
Lance Edgar 5907973d42 Refactor "edit printer settings" view for Label Profile
for sake of Buefy, but it was definitely using some old form patterns...
2019-05-23 17:58:46 -05:00
Lance Edgar a37b0229a0 Fix edit icon for row grids 2019-05-23 17:12:19 -05:00
Lance Edgar d7c8b80da5 Fix Buefy "row grids" when viewing parent; add basic file upload support 2019-05-23 16:29:29 -05:00
Lance Edgar 5998941741 Refactor Buefy forms a bit more, to copy grid pattern
i.e. each page can modify the component dynamically before it's registered
2019-05-23 14:52:22 -05:00
Lance Edgar 5bd4f84389 Accept disabled prop for <once-button> component 2019-05-23 14:44:31 -05:00
Lance Edgar 8a47ab2dde Punctuation tweak 2019-05-23 13:58:29 -05:00
Lance Edgar dda790b5c4 Fix datepicker behavior for grid filters
apparently we *do* need to accept a 'value' prop for tailbone-datepicker, to
round out the v-model support
2019-05-23 13:57:44 -05:00
Lance Edgar b829cd260c Assume forms support Buefy if theme does; fix basic CRUD views 2019-05-23 13:11:26 -05:00
Lance Edgar 7b1947914e Make Buefy grids use proper Vue.js component structure
at least, better than before...this lets each page have the final say about the
app logic etc.
2019-05-23 12:10:11 -05:00
Lance Edgar 6c3722737d OMG so many Buefy things...and much to be done yet it seems
these changes are all with Buefy "forms" support in mind.  hopefully didn't
break any legacy/jquery stuff... and yeah, lots more left to do still for the
sake of Buefy forms
2019-05-22 15:31:23 -05:00
Lance Edgar eea3f671af Add basic Buefy support for "Make User" button when viewing Person
this still relies on jQuery for now, but has Buefy styles at least...
2019-05-22 10:34:03 -05:00
Lance Edgar f4f435c682 Add <once-button> component for Buefy templates
i.e. just a button, which allows only one click and then auto-disables
2019-05-21 20:11:57 -05:00
Lance Edgar b16a81cf6e Keep using forms.css from bobcat theme, for falafel
..for now
2019-05-21 19:29:44 -05:00
Lance Edgar be6a1d916f Add Buefy support for enum grid filters 2019-05-21 18:40:08 -05:00
Lance Edgar ef7b2ddbdd Add basic Buefy support for default SelectWidget template 2019-05-21 17:54:14 -05:00
Lance Edgar d3471c945b Turn on Buefy forms for Email Bounce views 2019-05-21 17:39:26 -05:00
Lance Edgar 47b2c603ef Expose per-page size picker for grids 2019-05-21 17:37:39 -05:00
Lance Edgar 9a48a60d28 Add "full justify" for grid filter pseudo-column elements
at least the field name and verb columns, for now...
2019-05-21 16:18:12 -05:00
Lance Edgar 678c966113 Allow inherited theme to set location of Vue.js, Buefy etc.
that way, can use a local version instead of CDN
2019-05-21 16:00:56 -05:00
Lance Edgar d5d04b7dac Add support for Buefy datepicker in grid filters 2019-05-21 13:44:02 -05:00
Lance Edgar 0f0b32d797 Move logic used to determine if current request should use Buefy
so that function-based views can leverage it also
2019-05-21 12:34:18 -05:00
Lance Edgar fbf3ee5cd1 Improve readonly form templates somewhat, for Buefy
progress at least, more polish needed yet
2019-05-21 12:14:49 -05:00
Lance Edgar 40e957fff2 Let view template define how to render "row grid tools"
seems much cleaner that way.  must adopt Buefy to use this style though
2019-05-20 20:00:28 -05:00
Lance Edgar 3c8d16a368 Add custom tailbone-datepicker component for Buefy
for easier reuse, outside of main CRUD forms
2019-05-20 19:59:21 -05:00
Lance Edgar dfe0f49655 Add basic/generic Buefy support to the Form class
mostly just affects rendering, apparently backend logic needn't really change?
2019-05-20 16:24:14 -05:00
Lance Edgar a125e381a9 Add basic Buefy support for batch refresh, execute buttons
still doesn't yet handle the "execution options" use case though
2019-05-20 14:43:51 -05:00
Lance Edgar 93d0cfcfeb Make email preview buttons use primary color
i.e. for bulma/buefy
2019-05-20 14:20:54 -05:00
Lance Edgar 6ea4d9c413 Add default sort for PersonNote grid 2019-05-15 15:34:32 -05:00
Lance Edgar e6301f0d06 Add basic master view for PersonNote data model 2019-05-14 15:59:57 -05:00
Lance Edgar f61cf318ae Add verbose flag for util.raw_datetime() rendering
just seems like could be useful somewhere...though not used yet
2019-05-14 15:58:43 -05:00
Lance Edgar 13db4861e1 Update changelog 2019-05-09 12:34:18 -05:00
Lance Edgar 684363bcde Add basic/generic email validator logic
for use mostly in non-web scenarios, probably
2019-05-09 12:28:57 -05:00
Lance Edgar a8db5db308 Add basic Buefy form support when generating reports
apparently we have a lot of work to do yet for Buefy forms elsewhere...
2019-05-08 20:24:19 -05:00
Lance Edgar 4a198ce473 Tweak how we disable grid filter options
hoping to find some magic combo that works for everyone...
2019-05-08 17:16:27 -05:00
Lance Edgar e9976635ba Align pseudo-columns for grid filters; let app settings define widths 2019-05-08 17:13:01 -05:00
Lance Edgar 079680d72e Add simple_field() def for base falafel template
hopefully this is a useful abstraction which will allow for smoother transition
to Buefy-style fields, when the time comes?
2019-05-08 16:15:54 -05:00
Lance Edgar 4c3dc6362c Make "view profile" buttons use "primary" color
for sake of Buefy themes
2019-05-08 16:15:24 -05:00
Lance Edgar 98428bf8c2 Fix sorting info bug when Buefy grid doesn't support it 2019-05-08 15:37:03 -05:00
Lance Edgar 73eec8f112 Add support for "quickie" search in falafel theme 2019-05-08 14:19:20 -05:00
Lance Edgar 789512de55 Must still define "jquery theme" for falafel theme, for now
ugh, need to get rid of that ASAP
2019-05-08 13:40:27 -05:00
Lance Edgar 070d4fc43e Force unicode string behavior for left/right arrow thingies
...hopefully this fixes an error we're suddenly seeing on a staging server?
2019-05-08 13:21:43 -05:00
Lance Edgar fadf540422 Allow choosing report from simple list, when generating new
refs #6619
2019-05-08 13:00:09 -05:00
Lance Edgar 3cb803ffe3 Clean up falafel theme, move some parts to root template path 2019-05-07 21:10:48 -05:00
Lance Edgar 6ef217c546 Expose params and type key for report output 2019-05-07 17:15:52 -05:00
Lance Edgar 118f22c164 Update changelog 2019-05-07 15:01:45 -05:00
Lance Edgar b2b4e1bfbc Add basic Buefy support for merging 2 objects
i.e. special grid stuff, plus "merge" view
2019-05-06 21:43:59 -05:00
Lance Edgar 9d6cc86e60 Add basic Buefy support for row grids
possibly even "complete" support...guess we'll see
2019-05-06 19:53:59 -05:00
Lance Edgar a3ca6abb7a Add basic support for "quickie" search
a master view can "support" quickie search, which means it will setup a route
suitable for the quickie search form action.  and/or it can "expose" quickie
search which means it will actually show a quickie search form on its views
2019-05-06 18:34:42 -05:00
Lance Edgar 35158204c5 Update changelog 2019-05-05 20:15:42 -05:00
Lance Edgar 4c4cefde6d Add basic Buefy support for full "profile" view for Person 2019-05-04 03:19:40 -05:00
Lance Edgar ff9554adc1 Update changelog 2019-05-03 14:28:08 -05:00
Lance Edgar 303c741a10 Add basic support for "touching" a data record object
to trigger further datasync logic for it, etc.
2019-05-03 14:24:55 -05:00
Lance Edgar 6b2ba3a285 Update changelog 2019-04-30 20:39:58 -05:00
Lance Edgar 06bedf6cb4 Pass batch execution kwargs when doing that via subprocess
i.e. instead of the normal in-app method
2019-04-29 09:06:54 -05:00
Lance Edgar a5f04b6c7f Add filter for Vendor ID in Pricing Batch row grid 2019-04-25 21:29:10 -05:00
Lance Edgar 364257fe05 Update changelog 2019-04-25 15:40:12 -05:00
Lance Edgar 0d00bd746e Don't assume grid model class declares its title
that works for Rattail models, but not e.g. those from Onager
2019-04-25 15:39:30 -05:00
Lance Edgar 5c86ab38a4 Update changelog 2019-04-25 14:49:45 -05:00
Lance Edgar cb67a23d0a Add render_person() convenience method for MasterView 2019-04-25 14:46:11 -05:00
Lance Edgar 25c8edd81c Allow config to specify grid "page size" options 2019-04-23 22:56:38 -05:00
Lance Edgar 798a9893e9 Add category, family, report code support for generic product batch 2019-04-23 22:50:58 -05:00
Lance Edgar f8d26b4f8f Fix some issues with progress "socket" workaround for batches 2019-04-19 17:28:45 -05:00
Lance Edgar 2c1985bef3 Add support for generic "product" batch type 2019-04-19 13:23:21 -05:00
Lance Edgar 4a5f1ce19a Improve default people "profile" view somewhat 2019-04-19 11:20:45 -05:00
Lance Edgar efb1a73e88 Add basic Buefy support for "find user/role with permission X"
still not totally polished, but works as expected
2019-04-18 22:13:05 -05:00
Lance Edgar ea54ca6c11 Expose new code fields for pricing batch 2019-04-18 18:21:32 -05:00
Lance Edgar 1016b46243 Add "created by" and "executed by" grid filters for all batch views 2019-04-18 17:51:22 -05:00
Lance Edgar a66ea53743 Declare row fields for vendor catalog batches
also exposes new "allowance" fields
2019-04-18 17:02:24 -05:00
Lance Edgar 95fb78f645 Fix auto-disable action for new message form
i.e. we don't want auto-disable there, b/c template does its own thing
2019-04-18 15:30:08 -05:00
Lance Edgar 6d68b56c56 Add views for "new product" batches 2019-04-17 21:48:41 -05:00
Lance Edgar fcfc8b56bb Add basic Buefy support for App Settings page
also various buttons have been tweaked on some other "master view" pages
2019-04-17 14:55:27 -05:00
Lance Edgar e1ff4578e9 Improve logic used to determine if current theme supports Buefy
let settings define this per theme, but have sane defaults also
2019-04-16 15:44:02 -05:00
Lance Edgar e45dfd7351 More tweaks for Buefy support 2019-04-15 20:34:34 -05:00
Lance Edgar 4a92b05b57 Add Buefy support for email preview buttons 2019-04-15 19:54:17 -05:00
Lance Edgar a0cd1f4cd0 Add "most of" Buefy support for grid filters
still a couple of details to wrap up yet, but this is most of it!
2019-04-15 18:36:14 -05:00
Lance Edgar 23c38e33d4 Update changelog 2019-04-12 16:01:21 -05:00
Lance Edgar 80158ffa95 Add "view profile" helper for all person-related views 2019-04-12 15:54:56 -05:00
Lance Edgar 97345c9710 Add raw_datetime() function to tailbone.helpers module 2019-04-12 12:55:22 -05:00
Lance Edgar fdb76fc56c Add a bit more context for "view person profile" 2019-04-12 12:55:09 -05:00
Lance Edgar df43abf9d3 Hopefully fix style bug when new filter is added to grid
i.e. when user selects a new filter from dropdown, sometimes it would display
incorrectly, with everything "after" the checkbox appearing *below* instead of
to the right of it
2019-04-12 10:48:56 -05:00
Lance Edgar 6ae703dfd9 Add "view profile" for viewing *all* details of a given person at once
feature preview (easter egg) only for now though, lots of refinement to do yet
2019-04-10 16:46:16 -05:00
Lance Edgar ec70d85638 Add custom grid filter for phone number fields
and use it in various grid views
2019-04-10 14:20:36 -05:00
Lance Edgar 2bdcc4fe47 Can finally assume "simple" menus by default
all apps in the wild already using them
2019-04-02 21:55:54 -05:00
Lance Edgar c26af4758b Update changelog 2019-04-02 14:48:59 -05:00
Lance Edgar 511ba61b1c Add move_before() convenience method for GridFilterSet
to more easily rearrange sort order of grid filters
2019-04-02 14:44:59 -05:00
Lance Edgar bf189bb704 Use shipped instead of ordered, for receiving authority
i.e. compare receiving quantities to shipped quantities instead of ordered
2019-04-01 13:32:43 -05:00
Lance Edgar 49017fda39 Make sure user sees "receive row" page on mobile, after scanning UPC
was still redirecting to "view row" which is sort of deprecated now...
2019-04-01 13:31:57 -05:00
Lance Edgar 53917e9bf5 Require invoice parser selection for new truck dump child from invoice 2019-04-01 12:32:35 -05:00
Lance Edgar 503d508a86 Update changelog 2019-03-29 12:51:14 -05:00
Lance Edgar 8ee20e52f8 Add icon for Feedback button, in falafel theme 2019-03-29 00:29:16 -05:00
Lance Edgar 05b8ed7153 Add support for "row status" in Buefy grid tables 2019-03-28 23:09:10 -05:00
Lance Edgar 24547b4fc5 Add proper hamburger menu for falafel theme
fixes "disappearing menu" issue on mobile
2019-03-28 22:15:08 -05:00
Lance Edgar 18ad664acb Add validation when "declaring credit" for receiving batch row
i.e. don't just blindly attempt, when it isn't supported
2019-03-27 21:06:23 -05:00
Lance Edgar d60679adfd Don't allow deletion of some receiving data rows on mobile
specifically, rows on a truck dump parent, which originated from a child
batch (and therefore presumably, an invoice)
2019-03-27 20:11:32 -05:00
Lance Edgar 9ace36c459 Remove duplicate code
not sure how that got in there, oh well
2019-03-27 20:10:09 -05:00
Lance Edgar e9c9772c58 Fix HTML escaping bug when rendering products with pack price 2019-03-27 19:42:44 -05:00
Lance Edgar d20d22ffb6 Fix rendering bug when price.multiple is null 2019-03-27 19:39:58 -05:00
Lance Edgar a139d9c844 Add feature for generating new report of arbitrary type and params 2019-03-27 18:38:33 -05:00
Lance Edgar 13bba63382 Remove 'number' column for Customers grid by default 2019-03-24 21:09:12 -05:00
Lance Edgar 8d6ecc3ec7 Add basic "Buefy" support for grids (master index view)
still pretty experimental at this point, but making progress
2019-03-24 21:09:08 -05:00
Lance Edgar 3cef591719 Add support for one more package link in upgrade diffs 2019-03-22 20:02:36 -05:00
Lance Edgar 34a3aa0e3d Add smarts for a couple more projects in the upgraded packages links 2019-03-22 19:59:32 -05:00
Lance Edgar 1938884656 Update changelog 2019-03-21 19:34:15 -05:00
Lance Edgar 30ed84fd1d Allow width of object helper panel to grow 2019-03-21 19:30:10 -05:00
Lance Edgar b67414bb84 Update changelog 2019-03-14 13:37:49 -05:00
Lance Edgar 5b9e97b4eb Add "declare credit" UI for receiving batch rows 2019-03-13 19:15:53 -05:00
Lance Edgar c869516449 Add basic "receive row" desktop view for receiving batches
not terribly polished yet, but works
2019-03-13 18:31:57 -05:00
Lance Edgar 7fab472fc4 Add "time ago" for "product already receieved" alert on mobile 2019-03-13 12:49:54 -05:00
Lance Edgar f755aefbfa Honor enum sort order where possible, for grid filter values 2019-03-12 15:13:34 -05:00
Lance Edgar 43122381f5 Add mobile alert when receiving product for 2nd time
optional per config.  idea is to alert user so they don't accidentally
double-receive a given item
2019-03-12 14:25:40 -05:00
Lance Edgar d0b1cb527e Tweak how batch handler is invoked to remove row
also, removes some related logic which now lives in handler
2019-03-11 19:32:41 -05:00
Lance Edgar c78d6f2104 Update changelog 2019-03-11 13:26:46 -05:00
Lance Edgar eac2c2ddb2 Fix some unicode literals for base template
only necessary for python2, but we still must support that for now...
2019-03-11 13:17:26 -05:00
Lance Edgar 9b1efc3e45 Update changelog 2019-03-11 13:01:31 -05:00
Lance Edgar 512084194d Fix PO unit cost calculation for ordering row, batch 2019-03-11 12:20:56 -05:00
Lance Edgar 8bb09f5739 Begin to customize grid filters, for 'falafel' theme 2019-03-10 23:24:24 -05:00
Lance Edgar 0a68ff6dd0 Add 'falafel' theme, based on bobcat
but with more aggressive approach, includes no jQuery UI JS/CSS (and is
somewhat broken accordingly, for now)
2019-03-10 21:36:25 -05:00
Lance Edgar e18e2492af Fix script tag for dodo theme 2019-03-10 18:17:59 -05:00
Lance Edgar 5d04de936b Allow apps to set background color per request 2019-03-10 16:36:16 -05:00
Lance Edgar 9a85bd0edb Add basic 'dodo' theme
definitely not complete, but a decent feature preview
2019-03-09 23:19:59 -06:00
Lance Edgar 20b97a88c0 Tweak header styles for 'bobcat' theme 2019-03-09 23:06:33 -06:00
Lance Edgar eafe3737dc Refactor template content_title() and prev/next buttons feature
those were intertwined but now are a bit more separate, much better
2019-03-09 21:22:07 -06:00
Lance Edgar 9f743daf07 Fix login page styles for bobcat theme 2019-03-09 18:44:13 -06:00
Lance Edgar 291ec3aa04 Hide feedback dialog HTML
so user can't ever see it during page load
2019-03-09 18:33:08 -06:00
Lance Edgar 84f25ae91e Fix layout issues for bobcat theme, so footer sticks to bottom
i.e. even when page has little/no content
2019-03-09 18:32:43 -06:00
Lance Edgar 5516a11012 Fix navbar, footer background to match custom body background 2019-03-09 01:58:22 -06:00
Lance Edgar 316ed83047 Add view, edit links to vue.js users index 2019-03-09 01:52:07 -06:00
Lance Edgar 75bddc8777 Use configured background color for 'bobcat' theme 2019-03-08 23:30:36 -06:00
Lance Edgar d096909a95 Expose "true cost" and "true margin" columns for products grid 2019-03-08 14:33:57 -06:00
Lance Edgar 15c47fb593 Update changelog 2019-03-08 11:50:40 -06:00
Lance Edgar d337defb09 Expose new "calculated" invoice totals for receiving batch, rows 2019-03-07 17:05:25 -06:00
Lance Edgar 3760c3239f Improve display of purchase credit data
esp. within a receiving batch row
2019-03-07 12:21:50 -06:00
Lance Edgar 4a9b528c47 Only objectify address data if present
i.e. don't try to "remove" an address if no fields are present in form
2019-03-06 21:06:48 -06:00
Lance Edgar 60334229d5 Fix grid link logic some more...
should not show link if value is None
2019-03-06 21:03:43 -06:00
Lance Edgar 40c7e34014 Show grid link even when value is "false-ish"
saw a value of '0' get rendered with no link; this fixes
2019-03-06 18:22:12 -06:00
Lance Edgar 75bea75dce Update changelog 2019-03-06 13:20:48 -06:00
Lance Edgar d5efc51d61 Tweak the "incomplete" row filter for mobile receiving batch
this really is not ideal...hopefully good enough to limp along for a while yet
2019-03-05 11:03:24 -06:00
Lance Edgar 3789e4b3bd Don't require user name for anonymous feedback msg 2019-03-04 18:29:25 -06:00
Lance Edgar ef7466e0d5 Add mobile support for basic "feedback" dialog 2019-03-04 18:12:37 -06:00
Lance Edgar 006a7096ed Add ability to sort by Credits? column for receiving batch rows 2019-03-02 18:07:07 -06:00
Lance Edgar b7a026a7e8 Add "truck dump status" fields to receiving batch views
also refactor some code to use e.g. `batch.is_truck_dump_parent()` for clarity
2019-03-01 12:12:00 -06:00
Lance Edgar a2965d83af Remove 'truck_dump' field from mobile receiving batch view
not needed, and a bit redundant
2019-03-01 09:39:40 -06:00
Lance Edgar 05481f7828 Add new "receive row" view for mobile receiving
this frees us up to dumb-down the "view row" which thus far has been tasked
with actual receiving
2019-02-28 16:21:13 -06:00
Lance Edgar b1c77afc81 Remove logic for "receiving a row" and invoke handler instead
i.e. for receiving batch
2019-02-28 15:54:31 -06:00
Lance Edgar a5df9a2b3d Invoke handler when marking batch as (in)complete 2019-02-26 18:01:27 -06:00
Lance Edgar 0f5d668f86 Add "plain" date widget
to avoid deform.addCallback() JS for mobile forms

surely there's a better solution, but this works for now...
2019-02-22 20:46:54 -06:00
Lance Edgar 4b3e1c7b1b Update changelog 2019-02-22 16:03:34 -06:00
Lance Edgar 4b97b403d3 Treat empty string as null, for app settings field values 2019-02-22 15:58:55 -06:00
Lance Edgar 145e7f5529 Allow vendor field to be dropdown, for mobile ordering/receiving
based on config.  useful for apps which have very few vendors
2019-02-19 21:11:49 -06:00
Lance Edgar 19080924d5 Declare "is contact" for the Customers view
removes some duplicated code.  also this adds CustomerNote to version history
2019-02-19 20:14:10 -06:00
Lance Edgar 6a57e51f6b Add unique_id() validator method to Customer view 2019-02-19 18:11:52 -06:00
Lance Edgar e916d4f71f Add basic support for editing address for a "contact" record 2019-02-19 18:11:15 -06:00
Lance Edgar b0b551af82 Add basic support for "mobile edit" of records
specifically need to allow this for Customer records, for one app
2019-02-19 17:10:42 -06:00
Lance Edgar 37a8bfd6f5 Update changelog 2019-02-14 10:36:24 -06:00
Lance Edgar 489619c337 Refactor email settings/preview views to use email handler
still have one thing to refactor before this is really "done" though..
2019-02-13 20:52:57 -06:00
Lance Edgar bd43884a1d Improve validator for "percent" input widget 2019-02-13 15:15:47 -06:00
Lance Edgar d693c00c47 Update changelog 2019-02-12 14:06:25 -06:00
Lance Edgar caec354092 Remove usage of colander.timeparse() function
that did very little, and has been removed in latest colander release
2019-02-12 14:03:27 -06:00
Lance Edgar d2629e8925 Update changelog 2019-02-08 13:56:00 -06:00
Lance Edgar a45ce2ced2 Introduce support for "children first" truck dump receiving
still needs more testing to see what's left...
2019-02-06 16:50:40 -06:00
Lance Edgar 4af971b83c Update changelog 2019-02-06 13:27:53 -06:00
Lance Edgar 6cfc72c875 Add support for downloading batch rows as XLSX file 2019-02-05 18:18:02 -06:00
Lance Edgar 10c30cd21a Update changelog 2019-02-05 17:28:58 -06:00
Lance Edgar 13ec46b145 Add generic support for "enable/disable selection" of grid records 2019-02-05 10:49:54 -06:00
Lance Edgar 05bb8a2df0 Add support for background color app setting 2019-02-03 14:40:21 -06:00
Lance Edgar 2f048b45c9 Improve user form handling, to prevent unwanted Person creation
i.e. only create new person if name(s) were provided
2019-02-02 20:30:35 -06:00
Lance Edgar 38d0ef8542 Use app node title setting for base template 2019-02-02 20:30:14 -06:00
Lance Edgar d67a2e60fe Fix template bug when master view has no "delete selected" feature 2019-02-02 20:15:28 -06:00
Lance Edgar 22c71d832e Add template support for "delete selected objects" feature
whoops, should have been part of last commit
2019-02-02 19:43:47 -06:00
Lance Edgar 84fc3e7d50 Add support for "delete set" feature for main object index view
aka. "delete selected objects"
2019-02-02 19:34:36 -06:00
Lance Edgar 5ec11cf5b8 Update changelog 2019-01-31 16:50:09 -06:00
Lance Edgar 933ee88172 Improve rendering of enabled field for tempmon clients, probes
to make it more clear, when disabled
2019-01-29 17:21:48 -06:00
Lance Edgar d18302314d Update changelog 2019-01-28 15:50:09 -06:00
Lance Edgar eb78d79bb3 Update tempmon UI now that enabled flags are really datetime in DB 2019-01-25 19:36:13 -06:00
Lance Edgar 90e1baef50 Update changelog 2019-01-24 17:13:19 -06:00
Lance Edgar d028679077 Fix response header value, per python3 2019-01-24 17:11:09 -06:00
Lance Edgar 0c57183a2d Update changelog 2019-01-23 13:26:11 -06:00
Lance Edgar 4b9394fe6b Use empty string for "missing" department name, for ordering worksheet
that way the sorting works as expected (i.e. for python3)
2019-01-23 13:23:04 -06:00
Lance Edgar 595f857aa1 Update changelog 2019-01-22 15:07:01 -06:00
Lance Edgar ede9bfb17a Include robots.txt in the manifest
how did this manage to be left out all this time?
2019-01-22 14:13:34 -06:00
Lance Edgar 91bb71dc29 Update changelog 2019-01-21 17:17:19 -06:00
Lance Edgar 318e8645b2 Fix Excel download of ordering batch, per python3 2019-01-21 17:11:12 -06:00
Lance Edgar 5a96672d79 Log details of one-off label printing error, when they occur
needed for troubleshooting
2019-01-21 16:23:50 -06:00
Lance Edgar 1d744d4c26 Update changelog 2019-01-17 15:47:08 -06:00
Lance Edgar 8945dd75aa Convert all datetime values to localtime, for "download rows as CSV"
this probably will need to be more flexible at some point; this works for now
2019-01-15 15:49:54 -06:00
Lance Edgar 4413a61513 Update changelog 2019-01-11 15:15:34 -06:00
Lance Edgar 1dd42e1ab2 Fix products grid query when filter/sort has multiple ProductCost joins 2019-01-11 15:14:37 -06:00
Lance Edgar 1dc21d846e Update changelog 2019-01-10 18:05:17 -06:00
Lance Edgar 0e0b125d99 Make command configurable, for restarting tempmon-client
can even configure it per client if necessary
2019-01-10 18:01:23 -06:00
Lance Edgar 051cb71956 Let batch view customize logic for marking batch as (in)complete 2019-01-09 14:18:30 -06:00
Lance Edgar 98fc4608da Tweak batch view template "object helpers" for easier customization 2019-01-09 12:35:26 -06:00
Lance Edgar 5bdacc70e5 Update changelog 2019-01-08 12:45:25 -06:00
Lance Edgar d659e62fda Add custom widget for "percent" field
so that storage can use "traditional" (0.3612) format but UI can use
"human-friendly" format (36.12 %)
2019-01-08 12:18:48 -06:00
Lance Edgar 7bf509097f Update changelog 2019-01-07 16:19:53 -06:00
Lance Edgar b333b3f083 Turn off messaging-related menus by default
i.e. unless config declares support for messaging.  this can cause errors
though, if menus try to link to non-existing view routes
2019-01-07 16:18:45 -06:00
Lance Edgar 6277e0e372 Fix styles for master view_row template
per flexbox
2019-01-07 16:10:55 -06:00
Lance Edgar 10f594c774 Minor tweaks 2019-01-06 19:52:56 -06:00
Lance Edgar c53170fe84 Update changelog 2019-01-02 17:59:00 -06:00
Lance Edgar 1e42fe9de5 Only allow POST method for executing "results" for batch grid 2019-01-02 17:07:43 -06:00
Lance Edgar c064bb275f Expose vendor_id column in pricing batch row grid 2019-01-02 16:38:38 -06:00
Lance Edgar 1fb9ef4d58 Update changelog 2019-01-01 22:29:43 -06:00
Lance Edgar 0ea0ca240a Fix bug when making new member 2018-12-19 23:14:23 -06:00
Lance Edgar 512e74c493 Add basic master view for Members table 2018-12-19 23:07:32 -06:00
Lance Edgar c9d40abe96 Update changelog 2018-12-19 11:38:01 -06:00
Lance Edgar 2ed34dda15 Make sure custom field labels are shown for batch execution dialog 2018-12-18 20:23:13 -06:00
Lance Edgar 5151e2dd96 Expose label profile selection when editing label batch 2018-12-18 19:26:11 -06:00
Lance Edgar a637ba1e6b Add 'percent' as field type for Form; fix rendering of 'percent' for Grid
these both now assume the value obtained will be a "typical" percentage
decimal, i.e. 0.30130 instead of 30.130
2018-12-18 17:41:38 -06:00
Lance Edgar 3e9fdbacad Expose subdepartment for pricing batch rows 2018-12-18 16:50:49 -06:00
Lance Edgar 8d534691ac Add 'unit_cost' to Excel download for Products grid 2018-12-18 16:37:40 -06:00
Lance Edgar c7496d7018 Clean up some inventory batch UI logic; prefer units by default 2018-12-18 15:13:01 -06:00
Lance Edgar d61d9cc574 Add ability to make new pricing batch from input data file 2018-12-17 22:35:56 -06:00
Lance Edgar 6a643411a4 Fix some styles, per flexbox layout changes 2018-12-17 22:35:42 -06:00
Lance Edgar d369693f9f Add oneoff_import() helper method to MasterView class 2018-12-15 22:29:28 -06:00
Lance Edgar b4d1666bdf Add object_helpers() def to master/view template 2018-12-15 22:29:08 -06:00
Lance Edgar 2f2b36c91d Update changelog 2018-12-14 16:09:26 -06:00
Lance Edgar 10a8babed7 Fix some layout styles for master edit template
yay, flexbox
2018-12-14 16:08:18 -06:00
Lance Edgar 86117944e4 Update changelog 2018-12-13 13:01:33 -06:00
Lance Edgar 841dda903f Refactor product view template to use flexbox styles
finally, the layout is reasonably clean and should stay that way...
2018-12-12 15:07:18 -06:00
Lance Edgar 6907fbe844 Update changelog 2018-12-10 18:34:40 -06:00
Lance Edgar bef7a2af36 Expose new "sync me" flag for LabelProfile settings 2018-12-04 18:56:20 -06:00
Lance Edgar 8192b19858 Update changelog 2018-12-02 15:35:42 -06:00
Lance Edgar fe35986432 Expose old_price_margin field for pricing batch rows 2018-11-30 19:24:23 -06:00
Lance Edgar 358ac1592b Fix layout issues for "object-helper" element
thanks flexbox!
2018-11-30 17:53:00 -06:00
Lance Edgar e100e0ea72 Use 4 decimal places when calculating hours for worked shift excel download 2018-11-30 16:59:59 -06:00
Lance Edgar 61fa77b752 Remove some pointless tests
they became broken, but not worth fixing
2018-11-30 16:07:58 -06:00
Lance Edgar 3bc0ba73ee Fix app_title reference in mobile templates 2018-11-30 15:32:59 -06:00
Lance Edgar 6022ef9be3 Don't assume app has configured a tempmon DB engine
whoops
2018-11-29 14:52:44 -06:00
Lance Edgar c1eaf28812 Add support for top-level links for simple menus
also add 'messaging_enabled' to global template context, so can include (or
not) that stuff in the user menu
2018-11-29 14:51:57 -06:00
Lance Edgar 0eb394fb86 Add support for "simple" menus in base template
also flesh out the simple menu logic a bit, add separators etc.
2018-11-29 14:00:11 -06:00
Lance Edgar 291128b96f Refactor default and bobcat theme re: "context menu" styles
finally, we have that hugging the top right corner, without float!
2018-11-29 12:04:29 -06:00
Lance Edgar c88e1cca68 Force use of "simple" menus for bobcat theme
seems safe i think...
2018-11-29 01:55:29 -06:00
Lance Edgar b5083d32db Add basic support for "simple menus"
for sake of keeping those in common across various themes
2018-11-29 01:39:20 -06:00
Lance Edgar a76a7dd54c Add support for new Bulma 'navbar' menu for bobcat theme
unfortunately the /menu.mako can't be shared (yet?) so apps must maintain a
separate one if they wish to support this new theme.

also, now when changing app theme we totally clear the lookup object's template
cache.  this was necessary for sake of /menu.mako but seems to be 'safe' so far
2018-11-28 23:50:50 -06:00
Lance Edgar 0ba1d65b11 Use bulma-style notifications for bobcat theme
instead of previous one, which was sort of pseudo-jquery i guess?
2018-11-28 22:35:22 -06:00
Lance Edgar 1fa56aa683 Add Bulma-style footer to bobcat theme
also refactor HTML element tree in general, for sake of bulma
2018-11-28 22:23:02 -06:00
Lance Edgar 103f006cc0 Turn on Bulma CSS framework for 'bobcat' theme
still trying to match the default theme here, but only in spirit, and giving
priority to doing things "the bulma way" if possible
2018-11-28 21:39:46 -06:00
Lance Edgar 9913586155 Add basic 'excite-bike' theme
no one will want this, surely...  but useful for contrast
2018-11-28 19:57:10 -06:00
Lance Edgar 3a982f6e38 Fix head_tags() template inheritance bug
this broke lots of things!
2018-11-28 19:44:09 -06:00
Lance Edgar 23ce2fb33c Add description, notes to default form_fields for batch views 2018-11-28 18:15:48 -06:00
Lance Edgar 36f786f0eb Clean up how we configure DB sessions on app startup
not real sure if the old logic was a "problem" per se, but this cleanup seems
warranted and (fingers crossed) shouldn't break anything
2018-11-28 15:37:57 -06:00
Lance Edgar c56eadc49b Fix "delete object" form submit
not real sure why that broke, but this is a better pattern anyway
2018-11-28 15:37:35 -06:00
Lance Edgar 508359a939 Fix template references for app_title
make sure we only look to /base_meta.mako for that
2018-11-27 18:20:15 -06:00
Lance Edgar acaa83c31a Add some code comments 2018-11-27 17:53:24 -06:00
Lance Edgar ea0dc1ea19 Add template "theme" feature, albeit global
would be even better to let each user session have something different, but
alas this is all-or-nothing for now
2018-11-27 17:52:02 -06:00
Lance Edgar f05d50bce3 Allow user to choose which columns to show, for Vue.js users grid 2018-11-27 03:05:14 -06:00
Lance Edgar b7319fd152 Add patterns for joining tables in API list methods
i.e. we needed to do an *outer* join on User.person since that can be null
2018-11-27 02:21:38 -06:00
Lance Edgar 93aa96a339 Add support for sorting by person name in Vue.js Users grid 2018-11-27 01:49:39 -06:00
Lance Edgar 875f520710 Add basic FontAwesome support to new Vue.js table grid
i.e. for sortable column icons
2018-11-27 00:57:38 -06:00
Lance Edgar 02528aecc7 Tweak styles for "global title" in header 2018-11-26 22:58:21 -06:00
Lance Edgar 993d8c3b4e Add very basic Vue.js grid/index experiment for Users table 2018-11-26 22:07:30 -06:00
Lance Edgar 25e61cc8d5 Use empty string instead of null as fallback value, for pricing rows CSV 2018-11-26 19:25:32 -06:00
Lance Edgar d773043429 Allow override of products query when making batch from it
also, invoke handler properly when populating the batch (i.e. to include
setup/teardown)
2018-11-26 18:58:55 -06:00
Lance Edgar 3b54ab3e0b Add "min % diff" option for pricing batch from products query
refactor the "batch from query" a bit also, to allow for multiple batch type
options which represent the same underlying batch type.  (thought i needed
that, then realized i didn't, but seems safe to include.)
2018-11-25 20:14:49 -06:00
Lance Edgar d9e5eff23d Fix download filename when it contains spaces 2018-11-25 17:27:31 -06:00
Lance Edgar 4fa9ab3c6e Add better support for "make import batch from file" pattern 2018-11-22 20:26:28 -06:00
Lance Edgar 0375d66b91 Tweak default "model title" logic for master view
i.e. if view class doesn't declare one
2018-11-22 11:12:31 -06:00
Lance Edgar 4ad958b9d2 Fix bug in receiving template when truck dump not enabled 2018-11-21 23:03:07 -06:00
Lance Edgar de788423e1 Update comments per frozen webhelpers2_grid dependency
not sure yet if it's worth refactoring to new version? probably is though..
2018-11-21 21:19:02 -06:00
Lance Edgar 6b7631013d Remove some relationship fields when creating new Person 2018-11-21 19:56:01 -06:00
Lance Edgar 5c66eb5f4f Refactor API collection_get to work with vue-tables-2
https://github.com/matfish2/vue-tables-2
2018-11-20 20:49:13 -06:00
Lance Edgar 81db564e34 Delay import of sqlalchemy_filters project
since apparently we can't use this (and hence our new API) unless python3
2018-11-20 17:07:28 -06:00
Lance Edgar 46501b7caa Use sqlalchemy-filters package for REST API collection_get
just sorting and pagination so far though, no actual filters yet
2018-11-19 23:56:42 -06:00
Lance Edgar 3e8d6a27f1 Update changelog 2018-11-19 14:15:48 -06:00
Lance Edgar 4806d7e5fe Expose price_diff_percent, margin_diff for pricing batch row 2018-11-18 21:12:08 -06:00
Lance Edgar 342c7c3854 Move some label definitions for pricing batch rows 2018-11-18 20:47:24 -06:00
Lance Edgar 4a36ab827c Expose "suggested price" for pricing batch row view 2018-11-18 20:02:43 -06:00
Lance Edgar fded97d586 Don't add values to CSV row for undefined fields 2018-11-18 20:02:14 -06:00
Lance Edgar de6275003e Add vendor id, name to row CSV download for pricing batch 2018-11-18 19:36:28 -06:00
Lance Edgar f7e549b5fd Expose new Customer.wholesale flag 2018-11-17 19:26:13 -06:00
Lance Edgar e27debd452 Allow override of template for custom create views 2018-11-17 18:23:07 -06:00
Lance Edgar e3afb2c52a Add department, subdepartment "name" columns for products XLSX download 2018-11-14 10:46:23 -06:00
Lance Edgar fed42d4898 Add "200 per page" option for UI table grids 2018-11-14 10:38:08 -06:00
Lance Edgar b33c2fd0d0 Add simple price fields for product XLSX results download 2018-11-14 10:33:39 -06:00
Lance Edgar a9b60b3d4a Update changelog 2018-11-08 15:48:38 -06:00
Lance Edgar 20e654ddea Display "suggested price" when viewing product details
very basic support here...
2018-11-08 09:57:41 -06:00
Lance Edgar bdbb8e2a7d Make sure status field is readonly when creating new batch 2018-11-07 16:47:51 -06:00
Lance Edgar 37b9a81344 Add Grid.hide_columns() convenience method 2018-11-07 16:47:41 -06:00
Lance Edgar 21014c5013 Remove unwanted style for "email setting description" field
not sure why that was in there, but it broke some other pages sure enough.
will have to revisit whenever i see the "problem" on email settings page again
2018-11-07 16:46:55 -06:00
Lance Edgar 9daefed9b3 Detect non-numeric entry when locating row for purchase batch
i.e. don't try to convert to GPC if non-numeric
2018-11-07 13:08:59 -06:00
Lance Edgar 23a94ebfad Update changelog 2018-11-07 10:11:11 -06:00
Lance Edgar d1980aeed8 Add client IP address to user feedback email 2018-11-05 11:24:03 -06:00
Lance Edgar fec8ba28e2 Refactor API views a bit for sake of running as separate service
also add "proper" (sic) permission checks
2018-11-03 18:55:26 -05:00
Lance Edgar 9b61b05155 Add dependency for cornice 2018-11-03 17:15:23 -05:00
Lance Edgar ad35481234 Use Cornice for REST API viws
still very experimental at this point
2018-11-03 17:13:08 -05:00
Lance Edgar 31ae5eacd5 Tweak status code rendering for upgrades API view 2018-11-02 21:44:19 -05:00
Lance Edgar 22ef6aad7b Fix bug in upgrades API view, when upgrade has no status code 2018-11-02 21:38:06 -05:00
Lance Edgar e4a518c444 Remove some unwanted row grid labels
doing it that way makes customization harder..still need to revisit how best to
do that i guess
2018-11-02 18:59:46 -05:00
Lance Edgar b8fdce378f Add basic API endpoint for upgrades, partial pagination support
latter is still broken, but this much is a starting point i think
2018-11-01 00:34:28 -05:00
Lance Edgar 0c41395cfc Add very basic API views to expose customer, user tables
just so we can populate an "index grid table" in the UI, for now..
2018-10-29 20:16:14 -05:00
Lance Edgar f43b6db427 Add initial tailbone.api subpackage, with basic auth API views
lots more to do here! but hopefully this is a solid start
2018-10-27 15:17:48 -05:00
Lance Edgar 5222f44904 Update changelog 2018-10-25 21:20:37 -05:00
Lance Edgar 1123cbb728 Only show Restart Filemon button if so configured
otherwise everyone would need to include that view in their config
2018-10-25 17:52:00 -05:00
Lance Edgar 2131ea65cb Add button for restarting filemon
although this button shows up only on the datasync page, for now..
2018-10-25 17:47:43 -05:00
Lance Edgar fc8391c6d1 Use load mask even for first data fetch, for probe readings graph 2018-10-25 16:00:15 -05:00
Lance Edgar f086a2aa38 Add more time range options for viewing tempmon probe readings as graph 2018-10-25 15:57:25 -05:00
Lance Edgar 92c1b165fb Try to configure the 'pyramid_retry' package, if available
this is used (as of pyramid 1.9) for gracefully handling postgres restarts
2018-10-25 14:33:28 -05:00
Lance Edgar 3df1407073 Update changelog 2018-10-24 19:22:27 -05:00
Lance Edgar 05c33a4b34 Add ability to "transform" TD parent row from pack to unit item
to make "claiming" more straightforward
2018-10-24 18:52:49 -05:00
Lance Edgar 2bd107056c Add MasterView.render_product(), fix edit for pricing batch row 2018-10-23 17:20:47 -05:00
Lance Edgar b9da7e1b12 Allow individual App Settings to not be required; allow null
hopefully this does the right thing also, not saving null to the db when that
isn't needed etc.
2018-10-23 10:25:57 -05:00
Lance Edgar f1eba6a404 Update changelog 2018-10-19 23:01:54 -05:00
Lance Edgar 0e13e5606a Add very basic support for viewing probe readings as graph
can only view the last hour of readings, so far
2018-10-19 23:00:43 -05:00
Lance Edgar c6b2f831e5 Update changelog 2018-10-19 21:18:53 -05:00
Lance Edgar f26f42427f Don't include LargeBinary properties in default colander schema
actually, exclude any found in secondary properties...i.e. from relationship
2018-10-19 21:17:50 -05:00
Lance Edgar ed9f8a269c Update changelog 2018-10-19 20:33:13 -05:00
Lance Edgar fe2905e9df Add support for "appliance type" 2018-10-19 20:27:04 -05:00
Lance Edgar 7e28619e9d Don't include grid filters for LargeBinary columns 2018-10-19 20:26:33 -05:00
Lance Edgar e277a19f71 Hopefully, let the Grid class generate a default list of columns 2018-10-19 20:26:04 -05:00
Lance Edgar 78941ec8d9 Add thumbnail images to Appliances grid
guess we'll see how folks like this
2018-10-19 19:47:00 -05:00
Lance Edgar 4aa8f43a7e Add basic image upload support for tempmon appliances 2018-10-19 19:20:20 -05:00
Lance Edgar 40a8761feb Add support for new Tempmon Appliance table, etc. 2018-10-19 17:55:23 -05:00
Lance Edgar aa97a36167 Customize template for viewing probe details
probably still need to improve editing also?  we'll see
2018-10-19 16:49:47 -05:00
Lance Edgar daa304c613 Add new timeout fields for tempmon probe 2018-10-19 16:12:42 -05:00
Lance Edgar 67e7ba023a Update release task to use twine for upload 2018-10-18 23:36:08 -05:00
Lance Edgar af956c6c42 Update changelog 2018-10-18 23:33:22 -05:00
Lance Edgar 1bbf6c0940 Fix a dialog button for Chrome 2018-10-18 15:50:06 -05:00
Lance Edgar 8d1bd2adcd Fix API doc reference to renamed subscriber function 2018-10-17 20:05:25 -05:00
Lance Edgar ff8f1ee435 Update changelog 2018-10-17 19:44:22 -05:00
Lance Edgar fd15865d0b Remove some tests
ugh, this is the wrong direction, but seems practical in this case since the
tests didn't accomplish much
2018-10-17 19:44:22 -05:00
Lance Edgar 5a9fedbb9a Add basic Excel download support for Products table 2018-10-17 18:29:49 -05:00
Lance Edgar 79e71ec4ab Cache user permissions upon "new request" event
this avoids a ton of (often redundant) SQL queries when checking permissions
2018-10-17 12:50:02 -05:00
Lance Edgar 0ed3429cf7 Update changelog 2018-10-13 21:04:09 -05:00
Lance Edgar fd0760ed07 Add "hours as decimal" hover text for some HH:MM timesheet values
hoping this helps with random troubleshooting...
2018-10-12 19:05:14 -05:00
Lance Edgar 9e065541b9 Update changelog 2018-10-09 16:38:10 -05:00
Lance Edgar f36c1fbc3f Improve "length" (hours) column for Worked Shifts grid
web display shows "pretty" hours (e.g. 7:30) whereas the Excel export shows
"decimal" hours (7.50)
2018-10-09 16:19:55 -05:00
Lance Edgar 94ba18eaee Add basic Excel download support for raw worked shifts
also, tweak response per python3
2018-10-09 14:10:36 -05:00
Lance Edgar 362173ef10 Allow override of jquery source, for mobile base template 2018-10-06 21:04:21 -05:00
Lance Edgar 3f2c57c89f Allow override of jquery for base template 2018-10-06 20:51:04 -05:00
Lance Edgar e05a58bdee Add some helptext for various tempmon fields 2018-10-06 17:41:33 -05:00
Lance Edgar f17d7355e0 Auto-disable button when sending email preview 2018-10-05 19:58:58 -05:00
Lance Edgar 29e023096b Show tempmon readings when viewing client or probe
also make the probes list more helpful when viewing client
2018-10-05 19:29:26 -05:00
Lance Edgar 7650064b64 Fix bug when non-numeric entry given for mobile inventory "quick row" 2018-10-03 15:54:28 -05:00
Lance Edgar d50d3678ec Update changelog 2018-10-03 14:20:10 -05:00
Lance Edgar 848b727b11 Tweak how receiving rows are looked up when adding to the batch
i.e. locate the product first, and then try to find an existing row to match.
previously we looked for a row based on product key match only, and it could
cause new rows to be created for a product we already had in the batch (i.e. if
the product was located via some secondary lookup other than product key)
2018-09-29 14:24:03 -05:00
Lance Edgar 5e49c2709b Expose new disk_type field for tempmon client views 2018-09-28 19:15:33 -05:00
Lance Edgar 6c1d67c966 Expose notes field for tempmon client and probe views 2018-09-28 12:27:08 -05:00
Lance Edgar 66807a801b Add support for "archived" flag in Tempmon Client views 2018-09-28 12:22:43 -05:00
Lance Edgar c1f05bf014 Update changelog 2018-09-27 21:11:27 -05:00
Lance Edgar d458d699e6 Restrict (temporarily I hope) webhelpers2_grid to 0.1
until we can figure out what happened in their 0.9 release
2018-09-27 21:10:36 -05:00
Lance Edgar 6c309705a0 Update changelog 2018-09-26 17:02:11 -05:00
Lance Edgar 27d5a92fee Tweak purchasing / receiving UI a bit
rows with 'out of stock' status are yellow; improve some row filter labels
2018-09-25 19:12:19 -05:00
Lance Edgar 878486cdab Capture user input for mobile receiving, and move some lookup logic
i.e. most of the logic responsible for looking up an item from e.g. scanner
entry, now lives in the handler for easier customization
2018-09-25 17:50:16 -05:00
Lance Edgar ed5455089e Expose item_entry field for receiving batch row 2018-09-22 20:18:52 -05:00
Lance Edgar d7863c2572 Add speedbump by default when deleting any "row" record
also, allow deleting rows for truck dump child batch
2018-09-22 19:27:17 -05:00
Lance Edgar 4a610ba2e6 Misc. UI improvements for truck dump receiving on desktop
links back and forth between parent/child rows, a little help text etc.
2018-09-22 18:33:01 -05:00
Lance Edgar 255485296c Leverage alternate code also, for mobile product quick lookup 2018-09-21 19:58:08 -05:00
Lance Edgar 99688c1c77 Update changelog 2018-09-20 18:23:09 -05:00
Lance Edgar fb3105c099 Fix batch row status breakdown, for rows with no status 2018-09-20 18:22:36 -05:00
Lance Edgar c3637bc416 Update changelog 2018-09-20 16:20:49 -05:00
Lance Edgar 8c26b632fe Only show mobile "quick receive" buttons if product is identifiable 2018-09-20 16:15:45 -05:00
Lance Edgar 0b9fe2dfe7 Add simple row status breakdown when viewing batch 2018-09-20 15:58:45 -05:00
Lance Edgar 3b0292029d More basic field tweaks for mobile "view product" page 2018-09-19 20:42:10 -05:00
Lance Edgar 3a91ab6bec Fix price fields, add pref. vendor/cost fields for mobile product view 2018-09-19 19:11:59 -05:00
Lance Edgar 66f1ed0e41 Do quick lookup by vendor item code, alt code for mobile receiving
at least until we have to make that configurable etc.
2018-09-19 18:22:59 -05:00
Lance Edgar acd8c97afc Fix how we check config for mobile "quick receive" feature
at least hopefully this fixes it, and doesn't break anybody..
2018-09-19 17:16:15 -05:00
Lance Edgar be49ca6967 Add quick-receive 1EA, 3EA, 6EA for mobile receiving
but only when cases are allowed.  at least for now...should surely be more
configurable than we have it now
2018-09-19 17:11:16 -05:00
Lance Edgar 2939b53467 Show red background for mobile receiving if product not found 2018-09-19 17:00:45 -05:00
Lance Edgar 6fb78c5dde Add setting to show/hide product image for mobile purchasing/receiving 2018-09-19 16:42:50 -05:00
Lance Edgar 5b2f4127ea Fix bug when editing truck dump child batch row quantities
sometimes we need to "add" to an existing claim which has qty None
2018-09-19 11:00:10 -05:00
Lance Edgar c5fef6b954 Add unique check for "name" when creating new Role 2018-09-19 10:00:59 -05:00
Lance Edgar 84db66a60c Update changelog 2018-09-10 18:55:55 -05:00
Lance Edgar db0eee707a Fix default (status) filter for Employees grid 2018-09-06 20:37:06 -05:00
Lance Edgar 06b5f6c97c Update changelog 2018-08-24 13:42:06 -05:00
Lance Edgar a6a7d22ec1 Honor view logic when displaying Delete Row button for mobile receiving
also do not allow quick receive if receiving from scratch
2018-08-17 12:41:48 -05:00
Lance Edgar f0d8f79676 Revert "Try to retry InvalidRequestError from sqlalchemy"
This reverts commit 4d0223e305.

well, that didn't work
2018-08-17 00:24:51 -05:00
Lance Edgar 4d0223e305 Try to retry InvalidRequestError from sqlalchemy
not sure if this is a good idea, hopefully can find out in a moment
2018-08-17 00:21:01 -05:00
Lance Edgar 528c0f9622 Refactor sqlerror tween to add support for pyramid_retry
hopefully this doesn't break anything else..
2018-08-17 00:04:59 -05:00
Lance Edgar 56392ccdd0 Add "quick receive all" support for mobile receiving
i.e. quick receive button can now receive all/remainder of the ordered qty
2018-08-16 22:21:58 -05:00
Lance Edgar d4b2cf9943 Update changelog 2018-08-14 17:06:24 -05:00
Lance Edgar 950af8b5e0 Add "quick lookup" for mobile Products page
only if enabled, otherwise just shows the normal grid
2018-08-09 22:11:44 -05:00
Lance Edgar 21740ea2fd Show links to claiming rows for truck dump parent row 2018-08-09 15:59:57 -05:00
Lance Edgar 5e879a2d92 Remove some unused code for ordering worksheets 2018-08-07 22:42:48 -05:00
Lance Edgar 6ef5677dc5 Use invoice total, PO total as fallback, for mobile receiving list 2018-08-07 21:41:43 -05:00
Lance Edgar ac451757b4 Add support for editing "claim" quantities for truck dump child row
at least i think this gets it all...guess we'll see
2018-08-07 15:19:38 -05:00
Lance Edgar a348755be2 Hide 'ordered' columns for truck dump parent row grid
since that batch type is only concerned with receiving
2018-08-07 13:09:13 -05:00
Lance Edgar a24076f0ce Make sure we refresh batch status when adding a new row
b/c whether or not it has a product will affect batch status.

this also changes how we interpret UPC for unknown product, i.e. by default we
now assume it does *not* have a check digit and that we should calculate that.
probably just a matter of time before someone needs the opposite though..
2018-08-02 16:58:38 -05:00
Lance Edgar e0f7ba827f Update changelog 2018-07-31 14:11:42 -05:00
Lance Edgar cefadc7c27 Don't configure versioning when making the app
that is now happening as part of the `make_config()` call
2018-07-31 14:08:38 -05:00
Lance Edgar de6401c5db Update changelog 2018-07-30 11:53:40 -05:00
Lance Edgar e43f713a66 Various tweaks for arbitrary model view with "rows"
just needed these for a particular feature...
2018-07-26 21:35:15 -05:00
Lance Edgar 8d77111b06 Update changelog 2018-07-26 13:50:15 -05:00
Lance Edgar f6712a6686 Redirect to "view parent" after deleting a row
not sure why that was redirecting to "edit parent" before...weird
2018-07-26 13:33:21 -05:00
Lance Edgar 6af9440ed7 Fix permission group label for Ordering Batches
a minor annoyance, but consistency surely is better...
2018-07-25 16:23:32 -05:00
Lance Edgar d145ce5f6d Assign purchase to new receiving batch via uuid instead of object ref
the latter was apparently causing session flush and would create the "dummy"
batch in addition to the "real" one...
2018-07-25 12:46:51 -05:00
Lance Edgar 634a93061b Let mobile form declare if/how to auto-focus a field
and for mobile ordering, auto-focus the "units" field when editing a row
2018-07-24 21:29:52 -05:00
Lance Edgar 6b3e645c12 Allow skipping of tests during release
sometimes ya just gotta do it
2018-07-19 17:41:32 -05:00
Lance Edgar fba7c5f978 Update changelog 2018-07-19 17:39:29 -05:00
Lance Edgar 5db7d3776a Expose status etc. when editing upgrade, rename Email Settings
i.e. latter is renamed from Email Profiles, but within UI only for now
2018-07-18 21:06:07 -05:00
Lance Edgar 34bdd2ac84 Add (restore?) basic support for mobile receiving from PO 2018-07-18 16:25:54 -05:00
Lance Edgar 87ba8026e5 Don't use empty string as default setting value
should just fall back to None as per usual
2018-07-18 13:53:24 -05:00
Lance Edgar c2968fbe52 Don't save any App Settings for which value would not change
that lets us avoid writing "redundant" values to the database, whereas in fact
the underlying value may be coming from config file
2018-07-18 13:50:32 -05:00
Lance Edgar 117e52df23 Remove unwanted line 2018-07-18 13:24:51 -05:00
Lance Edgar 4e09b757c3 Add (admin-friendly!) view to manage some App Settings
which settings are available to this view will depend on the project's settings
module, similar to how the email settings work
2018-07-18 13:09:32 -05:00
Lance Edgar 012a06d8a6 Tweak some purchase batch logic per changes in rattail 2018-07-17 20:38:48 -05:00
Lance Edgar d8b45db331 Improve support for "receive from scratch" workflow, esp. for mobile
also try harder to make certain aspects easier to enable/disable via handler,
e.g. whether cases should be allowed as quantity input, or expired credits
should be a thing etc.
2018-07-17 19:55:15 -05:00
Lance Edgar a34a42d2b2 Refactor mobile receiving to use "quick row" feature
plus some other random things thrown in there, for good measure..
2018-07-16 20:40:29 -05:00
Lance Edgar 3cc8adba86 Improve basic mobile views for customers, people 2018-07-15 18:13:30 -05:00
Lance Edgar eccce1cabb Add runtime mobile flag for MasterView
will be false unless one of the mobile views are in effect...hopefully this is
a good idea
2018-07-15 18:12:21 -05:00
Lance Edgar d3e67ccbcd Fix how we check file size when reading stdout for upgrade
i guess sometimes we were getting a negative number there
2018-07-15 17:40:46 -05:00
Lance Edgar 45f19517d3 Add 'person' column for customers grid
but don't show it by default, for now?
2018-07-15 16:23:20 -05:00
Lance Edgar 259d123876 Traverse master class hierarchy to collect all defined labels
i.e. for forms and grids
2018-07-15 16:22:30 -05:00
Lance Edgar 0853fac66a Fix Person.customers readonly field for python 3 2018-07-15 16:22:03 -05:00
Lance Edgar 6fc517269f Don't make customer ID readonly when editing
i mean, custom apps are welome to, but seems a bit heavy-handed as default
2018-07-15 15:33:35 -05:00
Lance Edgar 0e57152888 Add product grid filters for "on hand", "on order" 2018-07-13 20:17:22 -05:00
Lance Edgar 935a6b2a68 Add basic autocomplete support for "quick row" feature 2018-07-13 19:12:39 -05:00
Lance Edgar 68bd3047c4 Add initial support for mobile "quick row" feature, for ordering
at least for now, ordering only, but hopefully much more soon...
2018-07-12 22:53:29 -05:00
Lance Edgar aa6e540abd Use upload time as default filter/sort for Trainwreck transactions
also show end time, upload time as grid columns
2018-07-11 13:30:48 -05:00
Lance Edgar 9caf0e2e1f Update changelog 2018-07-11 10:29:40 -05:00
Lance Edgar 8039af1c06 Fix cancel button for progress page
i.e. should actually cancel when clicked...
2018-07-11 10:10:06 -05:00
Lance Edgar 699536b1ab Add "?" for daily time sheet total if partial shift present 2018-07-10 17:45:33 -05:00
Lance Edgar 16ab8b6ffa Stop trying to be smart about "best fit" cases/units for receiving
i.e. just record amounts as provided by the user.  sometimes it is necessary
for the user to avoid "cases" altogether if they detect the "case quantity" to
be incorrect
2018-07-10 16:43:21 -05:00
Lance Edgar 477a34cfa7 Improve how cases/units, uom are handled for mobile receiving
last-used uom should be more or less sticky, etc.
2018-07-10 14:24:12 -05:00
Lance Edgar 147c65afe6 Try to be smart about how we update cases/units for receiving batch row
e.g. if you receive 1 CS (@ 12/CS) and then subtract 4 EA then you should wind
up with 8 EA for the row
2018-07-10 13:36:28 -05:00
Lance Edgar 2983ff7ba0 Highlight purchasing batch rows with "case quantity differs" status 2018-07-10 12:38:58 -05:00
Lance Edgar ed6f2f27cc Show "truck dump" info for applicable receiving batch page title 2018-07-10 11:39:22 -05:00
Lance Edgar 9dd6f8ef7d Tweak default page title for master view 2018-07-10 11:39:00 -05:00
Lance Edgar 053fc4eb55 Sort mobile receiving rows by last modified instead of sequence
because we now prefer to aggregate rows for that, at least by default
2018-07-10 09:06:22 -05:00
Lance Edgar 44663fe548 Fix bug for inventory batch when product not found 2018-07-09 21:28:36 -05:00
Lance Edgar c88d060fe0 Force user to count "units" and not "packs" for inventory batch
at least until we come up with something smarter...
2018-07-09 15:50:28 -05:00
Lance Edgar 469f9cf015 Update changelog 2018-07-09 14:29:34 -05:00
Lance Edgar 3dfdb26502 Improve basic support for unit/pack info when viewing product details 2018-07-08 00:01:14 -05:00
Lance Edgar 9149902c78 Remove deprecated "edbob" settings 2018-07-07 20:43:17 -05:00
Lance Edgar b464db5722 Change field ordering for customer form
so that default_email comes next to email_preference
2018-07-06 14:17:33 -05:00
Lance Edgar 8cadec9a16 Fix enum values for customer email preference grid filter 2018-07-06 14:15:33 -05:00
Lance Edgar 00a0e6fb11 Update changelog 2018-07-03 20:55:11 -05:00
Lance Edgar 9a0a280d7d Tweak how some "pack item" fields are displayed when viewing product 2018-07-03 20:47:32 -05:00
Lance Edgar ad5444d270 Update changelog 2018-07-03 18:57:34 -05:00
Lance Edgar 3cc789dda9 Fix batch action kwargs, so 'action' can be a handler kwarg
i.e. at least the handheld batch handler, accepts an 'action' kwarg for its
execute() method.  we had apparently broken that
2018-07-03 18:32:03 -05:00
Lance Edgar ac5a6c011b Fix batch file download link URL 2018-07-03 18:25:34 -05:00
Lance Edgar 6febd01e76 Don't read upgrade progress file if size hasn't changed
apparently that is possible sometimes?  or perhaps just an issue on python 3?
2018-07-02 12:06:09 -05:00
Lance Edgar 4c2f1aa4ed Update changelog 2018-06-29 14:23:29 -05:00
Lance Edgar 944e896196 Consider any integer greater than PG allows, to be invalid grid filter value
this feels pretty hacky...would be nice to come up with a better way
2018-06-29 12:56:22 -05:00
Lance Edgar 4ffd0df7c1 Update changelog 2018-06-28 15:17:44 -05:00
Lance Edgar 2ffb930f7f Fix how "unknown product" row is added to receiving batch 2018-06-28 12:27:40 -05:00
Lance Edgar 8d0dfd631b Show department column for receiving batch rows 2018-06-28 12:27:30 -05:00
Lance Edgar 350e901c2a Highlight "cost not found" as warning, for purchasing batch 2018-06-28 12:27:04 -05:00
Lance Edgar 1342d67746 Improve basic support for adding new product 2018-06-28 12:26:22 -05:00
Lance Edgar b9d699df84 Fix email preview logic per python 3
can't use filter() anymore
2018-06-28 12:25:44 -05:00
Lance Edgar 6b01a7e888 Add highlight for "cost not found" rows in purchasing batch 2018-06-27 18:40:22 -05:00
Lance Edgar 49f241a4b9 Accept invoice number when adding truck dump child from invoice file 2018-06-27 18:00:28 -05:00
Lance Edgar eeba784c32 Be smarter about when we sort receiving batch by most recent (for mobile)
i.e. only do so when *not* aggregating products, since that probably needs a
closer look first
2018-06-27 17:29:31 -05:00
Lance Edgar 0ccb6883f8 Don't aggregate product for mobile truck dump receiving
also sort batch rows by most recent, for receiver convenience
2018-06-27 17:26:38 -05:00
Lance Edgar da10c6503c Add support for new credit_total field for purchase credits 2018-06-27 15:20:20 -05:00
Lance Edgar b66af5903b Add invoice_total column for purchase credits grid
that probably isn't quite right, but at least is something
2018-06-27 15:08:53 -05:00
Lance Edgar 440a88aa0f Add overflow validation for cases/units in inventory batch desktop form 2018-06-27 14:52:55 -05:00
Lance Edgar ee1065bfdb Allow editing of unit cost for inventory batch row 2018-06-27 13:56:20 -05:00
Lance Edgar 076d3d8189 Add support for zero quantity for mobile inventory batch rows 2018-06-27 13:43:03 -05:00
Lance Edgar c1e2c5551c Allow zero quantity for inventory batch desktop entry form 2018-06-27 13:34:48 -05:00
Lance Edgar edbf7e6723 Fix bug when populating new batch 2018-06-27 12:19:34 -05:00
Lance Edgar 88a8922833 Update changelog 2018-06-27 10:27:14 -05:00
Lance Edgar 0c653b5ee3 Fix input validation for integer grid filter
sometimes a default is provided as int
2018-06-27 10:26:37 -05:00
Lance Edgar f92123c398 Tweak pip and "upgrade strategy" for tox 2018-06-14 20:24:14 -05:00
Lance Edgar ea8e52377c Update changelog 2018-06-14 20:21:19 -05:00
Lance Edgar 8387129eda Add workaround for using pip 10.0 "internal" API in upgrades view 2018-06-14 19:57:15 -05:00
Lance Edgar 93b3a5dab6 Change how date fields are handled within grid filters
don't set type="date" b/c that can trigger native browser datepicker
2018-06-14 19:37:50 -05:00
Lance Edgar eb1bb02dc5 Update changelog 2018-06-14 12:20:36 -05:00
Lance Edgar baeb9a558e Expose new exempt_from_gross_sales flags 2018-06-14 12:04:50 -05:00
Lance Edgar 8428790001 Use "known" label if possible when making new grid filters 2018-06-14 12:04:35 -05:00
Lance Edgar 7c46f10dd1 Add Excel results download for categories, report codes
also fix department field widget for categories
2018-06-13 21:02:21 -05:00
Lance Edgar b1b4e7e4ef Auto-size columns for Excel results download 2018-06-13 21:00:11 -05:00
Lance Edgar 51ff56eb4f Update changelog 2018-06-09 16:59:36 -05:00
Lance Edgar df9141ec4e Let config override sys.prefix when launching batch commands in subprocess 2018-06-08 11:41:40 -05:00
Lance Edgar e608c0b428 Allow products view to set some labels in costs grid 2018-06-07 16:03:17 -05:00
Lance Edgar bac82f47d8 Update changelog 2018-06-07 14:33:13 -05:00
Lance Edgar 44ff02b7af Add versioning workaround support for batch actions
* add `can_cancel` flag for progress page, hide button if set
* overhaul populate/refresh/execute to launch socket/subprocess if necessary
2018-06-07 12:40:25 -05:00
Lance Edgar cc6fa7058b Update changelog 2018-06-05 14:39:49 -05:00
Lance Edgar ae3f79e522 Set filter value renderer when setting enum for grid field 2018-06-05 14:38:52 -05:00
Lance Edgar 3688979b8f Add integer-specific grid filter
this was necessary for smarter handling of "invalid" input, e.g. '.645' is not
a good value when querying integer fields
2018-06-05 11:08:36 -05:00
Lance Edgar cac9de3cc7 Update changelog 2018-06-04 14:11:13 -05:00
Lance Edgar 2923585bd3 Expose new Vendor.abbreviation field 2018-06-01 15:03:42 -05:00
Lance Edgar 8b46c1e3f0 Expose 'hidden' flag for inventory adjustment reasons 2018-06-01 13:03:41 -05:00
Lance Edgar 46c8887c3e Set default column renderers for grid based on data types
guess this really just moves that logic so it happens earlier
2018-06-01 13:02:55 -05:00
Lance Edgar db645fb393 Add support for variance inventory batches, aggregation by product
kind of a rushed job but hopefully this is all good...
2018-06-01 12:49:01 -05:00
Lance Edgar 5bc4a1618b Tweak inventory batch view per new variance mode 2018-06-01 10:46:43 -05:00
Lance Edgar dc5ad6ce82 Show department instead of subdept by default, for products grid 2018-06-01 09:59:03 -05:00
Lance Edgar 1866d8dd07 Freeze pip version for sake of tox tests...
ugh this needs some better attention soon!
2018-05-31 12:06:09 -05:00
Lance Edgar d1224ac879 Update changelog 2018-05-31 11:59:27 -05:00
Lance Edgar a1249a21c2 Show 'variance' field when viewing inventory batch row 2018-05-31 11:11:08 -05:00
Lance Edgar c583e9734c Update changelog 2018-05-30 16:37:07 -05:00
Lance Edgar 75b48fdaae Fix handling of (missing) password when user is edited
was accidentally blanking them out, if no password was provided...
2018-05-30 16:31:19 -05:00
Lance Edgar 9ece43ce57 Add initial support for "variance" inventory batch mode
probably incomplete yet; needs testing
2018-05-30 11:48:39 -05:00
Lance Edgar a557ec614a Make sure count mode is preserved when making new inventory batch
i.e. even if only one count mode is allowed for the user
2018-05-29 13:10:45 -05:00
Lance Edgar 6c0f243655 Add basic docs for CSV download support in master view 2018-05-29 12:38:37 -05:00
Lance Edgar d03de66b64 Update changelog 2018-05-25 13:23:36 -05:00
Lance Edgar ccdf821583 Add MasterView.use_byte_string_filters flag for encoding search values 2018-05-24 15:09:00 -05:00
Lance Edgar 54bfafdbfe Add way to prevent "case" entries for inventory adjustment batch 2018-05-23 14:48:17 -05:00
Lance Edgar 57c2a7981f Fix some things for inventory batch views 2018-05-23 14:13:28 -05:00
Lance Edgar 62dca3d0b0 Only show "toggle complete" buttons when viewing batch
i.e. just show simple value for e.g. delete batch page
2018-05-23 13:28:11 -05:00
Lance Edgar 6d27d0cfba Hide "create new row" link for batches which are marked complete 2018-05-23 13:11:32 -05:00
Lance Edgar 218ac221e5 Add buttons to toggle batch 'complete' flag when viewing batch 2018-05-23 13:06:49 -05:00
Lance Edgar a4095b30f7 Add basic forms API doc 2018-05-22 20:29:07 -05:00
Lance Edgar c9eeabecba Add allow_zero_all flag for inventory batch master
defaults to true, but setting to false should disable "zero all" count mode
2018-05-22 20:18:47 -05:00
Lance Edgar 961e0e801d Increase allowed width for form labels 2018-05-22 18:35:00 -05:00
Lance Edgar 37a21d93a1 Add category name filter for products grid 2018-05-22 17:52:04 -05:00
Lance Edgar ecf7acc800 Fix handling of 'filename' field when making new batch 2018-05-22 15:31:31 -05:00
Lance Edgar b0e8f7d985 Various changes to support current receiving workflows
i.e. for sake of truck dump, adding child from invoice etc.
2018-05-22 13:54:50 -05:00
Lance Edgar 210508480e Add "Receive 1 CS" button for better efficiency in mobile receiving 2018-05-21 16:16:12 -05:00
Lance Edgar db25a5bfd0 Add docs for MasterView.help_url and get_help_url() 2018-05-21 15:27:22 -05:00
Lance Edgar e5ffe3025b Set received date for new truck dump batches, show when choosing parent 2018-05-18 17:21:01 -05:00
Lance Edgar cd7922f204 Add "most of" support for truck dump receiving
still not complete, but conceptually it sort of is...
2018-05-18 15:51:47 -05:00
Lance Edgar 805a1afa3f Fix rowcount bug when first row added via ordering worksheet 2018-05-16 09:44:16 -05:00
Lance Edgar 9ed501a8cc Add initial support for receiving truck dump batch via mobile
i.e. just the initial truck dump, but secondary invoice batches are not yet
supported.  also this maybe breaks other things..we'll see
2018-05-16 09:15:52 -05:00
Lance Edgar b515331e48 Allow lookup of inventory item by alternate code
i.e. in addition to UPC.  but only if so configured
2018-05-09 15:58:09 -05:00
Lance Edgar 177d9d2e3d Fix label profile type field when editing label batch row 2018-05-09 15:12:59 -05:00
Lance Edgar 4ee30feb0f Fix bug for purchase batch 2018-05-03 18:20:38 -05:00
Lance Edgar a5d1eece71 Improve default behavior for receiving a purchase batch
only targeting desktop so far, mobile is next...
2018-05-03 18:15:35 -05:00
Lance Edgar e6144ea08b Add Form.__contains__() method
for testing if a field is contained in the form
2018-05-03 10:54:40 -05:00
Lance Edgar 497c80161d Update changelog 2018-05-02 10:38:07 -05:00
Lance Edgar c869238678 Add sort/filter for department name, for Categories grid 2018-05-02 10:37:17 -05:00
Lance Edgar 7f567dec3a Update changelog 2018-04-12 19:31:45 -07:00
Lance Edgar 8c8d539266 Add future mode for vendor catalog batch 2018-04-10 09:07:57 -07:00
Lance Edgar 8ea769d0e5 Update changelog 2018-04-09 13:17:45 -07:00
Lance Edgar 7443b31a93 Add new vendor catalog row status, render product with hyperlink 2018-04-06 09:50:37 -07:00
Lance Edgar 8c211df633 Add awareness for Email.dynamic_to flag in config UI
i.e. show help text and do not allow edit, when relevant
2018-04-01 17:14:00 -07:00
Lance Edgar eb45b9f8d9 Update changelog 2018-03-23 10:33:56 -05:00
Lance Edgar d550efbf8f Fix default selection bug for store/department time sheet filters 2018-03-21 13:55:21 -05:00
Lance Edgar e9322628cb Refactor inventory batch desktop lookup, to allow for Type 2 UPC logic
for now though, such logic must be provided by custom app
2018-03-21 11:30:14 -05:00
Lance Edgar 42982a69ea Treat unknown UPC as "product not found" for inventory batch
i.e. as opposed to collecting info about the product
2018-03-21 10:52:30 -05:00
Lance Edgar fde5398455 Use 'today' as fallback date for ordering worksheet 2018-03-21 10:42:50 -05:00
Lance Edgar 79b1502920 Update changelog 2018-03-15 16:39:24 -05:00
Lance Edgar bce6662eae Fix autodisable button bug for forms marked as such 2018-03-15 15:54:16 -05:00
Lance Edgar 69f04beb6d Fix text area behavior for email recipient fields 2018-03-12 18:27:50 -05:00
Lance Edgar 85f108d10e Update changelog 2018-03-12 17:58:41 -05:00
Lance Edgar 652f51d484 Add support for making new product on-the-fly during mobile ordering
let's face it, that will be necessary sometimes.  this feature still needs some
work before can be called complete though...
2018-03-06 19:29:15 -06:00
Lance Edgar 6ec0ddb94e Remove the "add vs. subtract" mode for desktop inventory workflow form
hopefully we can always assume the "mode" based on other things
2018-03-06 16:26:53 -06:00
Lance Edgar 802f4bfd6b Add disable_submit_button() global JS function
managed to find another use for this, so split it out
2018-03-06 16:26:15 -06:00
Lance Edgar 5765533491 Add changelog link for rattail-tempmon in upgrade diff 2018-03-05 20:26:22 -06:00
Lance Edgar aeccf5c5f6 Fix default create logic for vendors, products
online demo triggered errors for this.  might as well have basic support
2018-03-05 20:20:35 -06:00
Lance Edgar 90f0fcfea6 Expose vendor item code for purchase credits
also, fix some issues with mobile receiving logic
2018-03-01 15:16:40 -06:00
Lance Edgar 91bb38573b Add desktop support for creating inventory batches
with a workflow form of sorts
2018-02-28 21:53:39 -06:00
Lance Edgar 52e9717288 Update changelog 2018-02-27 19:12:46 -06:00
Lance Edgar bd45833395 Test commit 2018-02-26 00:49:21 -06:00
Lance Edgar c7c241fe7a Update trove classifiers to reflect python 3, beta status 2018-02-25 21:25:52 -06:00
Lance Edgar 73f73e0236 Add python 3.5 support for tox; run all tests before a release 2018-02-24 17:15:49 -06:00
Lance Edgar 021848524a Fix field type for Trainwreck view 2018-02-22 21:04:00 -06:00
Lance Edgar 2c2df9f01e Fix bug in users view when person field not present 2018-02-22 13:25:20 -06:00
Lance Edgar f2a60f683c Add logic for editing default phone/email in base master view
and refactor customer, vendor views to use it
2018-02-22 12:27:08 -06:00
Lance Edgar 630ffe0cf8 Don't allow row deletion if batch is marked complete 2018-02-22 12:26:21 -06:00
Lance Edgar 3d79f9fd7d Add support for executing batch with options, via mobile 2018-02-22 11:20:12 -06:00
Lance Edgar 0a165c5b93 Don't set order date for new ordering batch when created via mobile
that really should be set upon batch execution instead
2018-02-22 11:19:33 -06:00
Lance Edgar 2a2ff721c1 Bind batch to its execution options schema, when applicable
so the batch can provide default values, etc.  this also tweaks logic for using
defaults from session storage, so that they don't take priority over batch values
2018-02-22 11:18:11 -06:00
Lance Edgar d75fe88c44 Expose ship_method and notes_to_vendor for purchase, ordering batch 2018-02-22 11:16:33 -06:00
Lance Edgar 37a788a141 Use Form.submit_label if present, or fall back to save_label
latter should probably be deprecated / removed at some point
2018-02-22 11:14:21 -06:00
Lance Edgar e1a9da0716 Always show flash-error-style message when form has errors
probably will regret this and change it back soon, we'll see
2018-02-22 11:13:29 -06:00
Lance Edgar ff7341d272 Add Form.mobile flag and set link button styles accordingly 2018-02-22 11:12:51 -06:00
Lance Edgar 046a70c5f6 Add NumberInputWidget for <input type="number" /> 2018-02-21 19:51:31 -06:00
Lance Edgar a8a4e362a0 Add basic mobile support for executing batches
no progress, or options, yet..
2018-02-21 18:55:16 -06:00
Lance Edgar 6ca69802f5 Add download path for batch master views 2018-02-19 19:59:01 -06:00
Lance Edgar 1b059c5293 Refactor ordering worksheet to use shared logic 2018-02-19 18:19:19 -06:00
Lance Edgar b529a005d8 Remove some redundant / unused code 2018-02-19 17:09:12 -06:00
Lance Edgar 12dd6ae6b0 Use all "normal" product form fields, for mobile view 2018-02-19 15:31:02 -06:00
Lance Edgar e93e1b91a9 Update changelog 2018-02-15 18:49:16 -06:00
Lance Edgar 5c1008a0df More tweaks for python 3 2018-02-15 12:48:14 -06:00
Lance Edgar 135e98cde1 Fix encoding bug for python 3, when downloading CSV results 2018-02-14 15:27:55 -06:00
Lance Edgar 79634d402e Update changelog 2018-02-14 14:18:38 -06:00
Lance Edgar cb2234cef5 Fix encoding for robots.txt view response 2018-02-14 10:31:04 -06:00
Lance Edgar cfb6cf5ab4 Tweak rendering for python 3 2018-02-14 09:52:19 -06:00
Lance Edgar 0a16cc2ded Add tailbone version to base stylesheet URLs
hopefully this forces clients to refresh after upgrade?
2018-02-13 00:10:32 -06:00
Lance Edgar a0d9b5ddf4 Add generic 'login_as_home' setting
i.e. redirect anonymous users to login instead of showing home page
2018-02-12 22:18:59 -06:00
Lance Edgar 2ab00bfd78 More python 3 tweaks 2018-02-12 22:17:38 -06:00
Lance Edgar f411dcde24 Remove pyramid_debugtoolbar dependency 2018-02-12 21:25:58 -06:00
Lance Edgar 585db147ac Tweak dependencies per rattail changes 2018-02-12 19:39:04 -06:00
Lance Edgar 17d99e16b9 More tweaks for python 3 2018-02-12 19:22:05 -06:00
Lance Edgar b0821e8011 More tweaks for python 3 2018-02-12 15:32:54 -06:00
Lance Edgar ee35cc6f22 Misc. cleanup for Python 3 2018-02-12 14:41:40 -06:00
Lance Edgar 189bc1faa8 Officially remove pyramid_simpleform dependency 2018-02-12 12:16:56 -06:00
Lance Edgar d9ff59afda Refactor grid filters to use colander/deform 2018-02-12 12:15:07 -06:00
Lance Edgar f636b98cb3 Officially remove FormEncode dependency 2018-02-11 23:33:09 -06:00
Lance Edgar 33e345f4ae Officially remove FormAlchemy dependency (yay!) 2018-02-11 23:25:54 -06:00
Lance Edgar cb8db266cd Remove last references to any "fieldset" type things 2018-02-11 23:19:30 -06:00
Lance Edgar cdaf36f346 Rename 'forms2' package, templates to 'forms' 2018-02-11 22:57:33 -06:00
Lance Edgar d0b78babd2 Remove legacy 'forms' package and templates
yay!
2018-02-11 22:46:35 -06:00
Lance Edgar 66769ab34b Stop configuring FormAlchemy engine etc. on app startup 2018-02-11 22:41:20 -06:00
Lance Edgar dd04459748 Refactor batch execution options to use colander/deform 2018-02-11 22:37:17 -06:00
Lance Edgar 2cbacd6187 Remove legacy fieldset configuration logic 2018-02-11 16:25:09 -06:00
Lance Edgar 1c27efc8f1 Refactor feedback feature to use colander/deform 2018-02-11 16:05:56 -06:00
Lance Edgar 4191e50456 Refactor time sheet, schedule filter forms to use colander/deform
also add "print employee schedule" feature, didn't realize that was missing
2018-02-11 15:58:06 -06:00
Lance Edgar d30e5e2b02 Update changelog 2018-02-10 20:22:19 -06:00
Lance Edgar 4191a56bfb Fix some bugs with importer batch views 2018-02-10 17:14:32 -06:00
Lance Edgar ec438ead51 Refactor user login, change password to use colander/deform 2018-02-10 16:47:53 -06:00
Lance Edgar cff757fe9e Refactor mobile inventory to use colander/deform 2018-02-10 14:07:16 -06:00
Lance Edgar a65235c0fd Refactor mobile receiving to use colander/deform 2018-02-10 14:00:28 -06:00
Lance Edgar ec275b2fe0 Remove tests for legacy forms 2018-02-10 02:29:42 -06:00
Lance Edgar 91b395118e Allow passing arbitrary attrs when rendering grid 2018-02-09 21:13:41 -06:00
Lance Edgar e2bfb31cb2 Add 'gridcore' jQuery plugin, for core behavior
also add 'selected' status for checkbox grids, etc.
2018-02-09 15:17:29 -06:00
Lance Edgar a3b2fbadb7 Make sure each grid has unique set of actions 2018-02-09 15:04:57 -06:00
Lance Edgar 4760295d6a Add some basic ORM object field types for new forms 2018-02-09 15:04:22 -06:00
Lance Edgar 035a7b2096 Add 'newstyle' behavior for Form.validate() 2018-02-09 15:03:44 -06:00
Lance Edgar c35bfa3e4e Let forms choose *not* to auto-disable their cancel button 2018-02-07 20:06:35 -06:00
Lance Edgar 00a3b8fc33 Make it easier to hide buttons for a form 2018-02-07 19:28:54 -06:00
Lance Edgar 9e9a5f9a6a Update changelog 2018-02-07 15:10:44 -06:00
Lance Edgar 9ad8e5b546 Add even better UPC validation for mobile receiving 2018-02-06 12:57:27 -06:00
Lance Edgar 44dec830e5 Add better UPC validation for mobile receiving 2018-02-06 12:53:29 -06:00
Lance Edgar 5b4718fac4 Avoid "auto disable" button logic for new message form 2018-02-06 11:23:28 -06:00
Lance Edgar 22236e2909 Add master view for EmailAttempt 2018-02-06 10:31:44 -06:00
Lance Edgar 9387ef7116 Fix missing import bug 2018-02-06 10:31:36 -06:00
Lance Edgar 2219315ccc Collapse all master4 views back to just 'master' 2018-02-05 21:23:23 -06:00
Lance Edgar 7c62b6f7a7 Remove unused reference to legacy forms 2018-02-05 18:25:45 -06:00
Lance Edgar 7730080afc Let each form define its "save" button text
where applicable etc.
2018-02-05 16:53:17 -06:00
Lance Edgar 6cc509f5b4 Add Form.show_cancel flag, for hiding that button
also use fields from schema by default, if fields not provided
2018-02-05 14:24:49 -06:00
Lance Edgar 22d9981c2e Use master4 for custorder views
guess i missed that one...
2018-02-05 14:24:21 -06:00
Lance Edgar 8137d715df Refactor purchasing batch views per master4 2018-02-05 13:00:34 -06:00
Lance Edgar dfc5e0f50e Refactor importer batch views per master4 2018-02-05 13:00:34 -06:00
Lance Edgar 4ab41ba82e Refactor trainwreck views per master4 2018-02-05 13:00:34 -06:00
Lance Edgar 38afb35b65 Refactor pricing batch views per master4 2018-02-05 13:00:34 -06:00
Lance Edgar e78d1ac3c1 Refactor inventory batch views per master4 2018-02-05 13:00:34 -06:00
Lance Edgar 533b491124 Refactor purchase views per master4 2018-02-05 13:00:34 -06:00
Lance Edgar 7cee515187 Refactor handheld batch views per master4 2018-02-05 13:00:34 -06:00
Lance Edgar 0737faa034 Refactor label batch views per master4 2018-02-05 13:00:33 -06:00
Lance Edgar 88fe195615 Refactor all "easy" views per master4 2018-02-05 13:00:33 -06:00
Lance Edgar 410ee8eb65 Add base master4 batch view 2018-02-05 13:00:31 -06:00
Lance Edgar 63290154eb Add master4, refactor customers view to use it 2018-02-05 13:00:28 -06:00
Lance Edgar ab16ffc823 Add "hidden" concept for form fields
i.e. include hidden fields but don't show label or other dressing
2018-02-05 13:00:28 -06:00
Lance Edgar 868b184069 Add 'plain' and 'jquery' templates for deform select widget
need to refactor things to get all that straight, at some point
2018-02-04 15:03:33 -06:00
Lance Edgar 7b4f7d758e Add setting for "force home" mobile behavior 2018-02-04 15:03:33 -06:00
Lance Edgar aded59d7ff Don't process file for new batch unless field is present 2018-02-03 14:49:49 -06:00
Lance Edgar 737b2e578a Show year dropdown by default for jQuery UI date pickers 2018-02-03 14:49:32 -06:00
Lance Edgar 33931b4bf2 Refactor vendor invoice batch views to use master3 2018-02-03 12:37:24 -06:00
Lance Edgar 97c5e97ccb Add append() and replace() methods for core Grid class 2018-02-02 13:16:25 -06:00
Lance Edgar 1e8c9f709b Update changelog 2018-02-01 14:30:22 -06:00
Lance Edgar c74bce2fdb Fix bugs when making inventory batch on mobile 2018-02-01 14:28:24 -06:00
Lance Edgar d35dc5582e Add proper enum for inventory batch "count mode" filter 2018-02-01 14:12:04 -06:00
Lance Edgar 46aa7d5824 Update changelog 2018-01-31 17:04:50 -06:00
Lance Edgar 34ea572c0b Cap zope.sqlalchemy dependency at pre-1.0
not sure why exactly but this is necessary for now
2018-01-31 15:09:52 -06:00
Lance Edgar 5f8e26a8ec Update changelog 2018-01-30 11:10:48 -06:00
Lance Edgar e821b2a025 Always redirect to mobile home page, if "other" page is refreshed
also applies when becoming / stopping root, and maybe other cases?
2018-01-29 22:47:30 -06:00
Lance Edgar 9beb32cea2 Fix mobile logout behavior 2018-01-29 18:07:29 -06:00
Lance Edgar 024f09dbd4 Fix permission bug when adding row in mobile receiving 2018-01-29 17:56:36 -06:00
Lance Edgar beadc08002 Update changelog 2018-01-29 17:13:03 -06:00
Lance Edgar 19cd6336f9 Refactor vendor catalog batch views to use master3 2018-01-28 01:26:51 -06:00
Lance Edgar 34e81dc50a Refactor inventory batch views to use master3 2018-01-27 20:13:01 -06:00
Lance Edgar e0650d26cf Refactor email profiles view to use master3 2018-01-27 19:16:07 -06:00
Lance Edgar 8d62960548 Refactor importer batch views to use master3 2018-01-27 13:04:07 -06:00
Lance Edgar 2cbe1b0049 Refactor pricing batch view to use master3 2018-01-27 12:53:32 -06:00
Lance Edgar 8eab3c5b36 Refactor handheld batch views to use master3 2018-01-27 12:23:47 -06:00
Lance Edgar eac59ba5c8 Refactor purchasing batch views to use master3 2018-01-27 11:59:52 -06:00
Lance Edgar d20601c359 Refactor label batch view to use master3 2018-01-26 19:14:15 -06:00
Lance Edgar efdbc3c5b5 Show "buttons" when viewing an object, with forms2
also tweak logic when creating a batch..we'll see if it works..
2018-01-26 19:04:34 -06:00
Lance Edgar 580f817dd9 Add vendor links in cost grid when viewing product 2018-01-26 18:30:29 -06:00
Lance Edgar e5c5a071f2 Add generic "download results as XLSX" feature 2018-01-26 14:24:06 -06:00
Lance Edgar eaad87c704 Add 'single' context var when rendering timesheet template 2018-01-26 12:01:58 -06:00
Lance Edgar 1453d33123 Add support for extra column(s) in timesheet view table 2018-01-25 17:02:53 -06:00
Lance Edgar b2b3a633d0 Show new 'exposed' field for brands table 2018-01-25 16:09:49 -06:00
Lance Edgar e8dfe92be3 Improve case/unit quantity validation for order worksheet 2018-01-25 15:07:16 -06:00
Lance Edgar 37de777b2a Show "unit cost" column by default, for products grid 2018-01-25 14:45:06 -06:00
Lance Edgar 62d1918892 Add FieldList wrapper for grid columns list
needs to be merged with the "forms2" equivalent at some point...
2018-01-25 14:43:30 -06:00
Lance Edgar f32cf3342c Allow disabling auto-dismiss of flash messages on mobile 2018-01-25 14:15:25 -06:00
Lance Edgar 96e5c42795 Add support for detaching Person from Customer 2018-01-24 23:53:12 -06:00
Lance Edgar b8c6e95b73 Update changelog 2018-01-24 18:20:19 -06:00
Lance Edgar 8044039d78 Add 'delete-instance' class to delete link when viewing a record
so that JS can watch its click event
2018-01-24 13:12:42 -06:00
Lance Edgar 440cfd0d72 Show new cashback field for Trainwreck transaction 2018-01-23 20:08:24 -06:00
Lance Edgar 04d1e303be Let custom inventory batch view override logic for mobile UPC scanning 2018-01-23 19:00:33 -06:00
Lance Edgar eefc3b33d7 Fix some master3 edit issues for products view 2018-01-23 17:41:01 -06:00
Lance Edgar 96421ee1e5 Fix doc reference 2018-01-22 17:27:58 -06:00
Lance Edgar 3d18460d23 Update changelog 2018-01-22 16:31:19 -06:00
Lance Edgar a428bebda9 Add basic "config" concept doc 2018-01-22 16:30:09 -06:00
Lance Edgar 18af33c9bb Add basic support for per-page help URL 2018-01-18 11:47:11 -06:00
Lance Edgar 80e9a9cf1c Add creates_multiple flag for master view 2018-01-18 11:46:10 -06:00
Lance Edgar a542cd70da Add option for preventing new inventory batch rows for unknown products 2018-01-17 14:55:17 -06:00
Lance Edgar dd7c2a0763 Warn if user "scans" UPC with more than 14 digits, for mobile inventory
never assume such a UPC is valid, warn instead of adding batch row
2018-01-17 14:45:09 -06:00
Lance Edgar 07e7c5c4a0 Update changelog 2018-01-16 13:24:10 -06:00
Lance Edgar bbcba05fdd Add placeholder doc for Configuration 2018-01-16 11:32:36 -06:00
Lance Edgar 7af29a243b Add concept doc for data batches 2018-01-16 00:22:46 -06:00
Lance Edgar 5e45d68bef Add basic schema docs 2018-01-15 23:19:29 -06:00
Lance Edgar c7af97f301 Add docs for app structure, and creating new project 2018-01-15 21:37:10 -06:00
Lance Edgar 0675be8835 Allow editing of inventory batch count mode and reason code 2018-01-15 16:01:46 -06:00
Lance Edgar 8291c4d273 Fix client field when creating / editing tempmon probe 2018-01-15 15:21:29 -06:00
Lance Edgar f9d1d34763 Fix bug when locating association proxy column 2018-01-15 15:11:12 -06:00
Lance Edgar c996bf47ea Update changelog 2018-01-11 15:29:40 -06:00
Lance Edgar dfd43b55aa Allow passing None to Form.set_renderer()
i.e. to remove any renderer which has been set
2018-01-11 15:17:58 -06:00
Lance Edgar f021df446c Let custom schema node start out with empty children
sometimes that's just necessary
2018-01-11 15:15:12 -06:00
Lance Edgar f4aa83788b Fix tox dependency for docs (for real) 2018-01-11 13:17:17 -06:00
Lance Edgar e334564520 Show case quantity for inventory batch rows 2018-01-11 13:07:10 -06:00
Lance Edgar 0e0ebe9251 Fix some mobile view URLs 2018-01-11 12:38:44 -06:00
Lance Edgar 4147752672 Fix dialog button click event when executing price batch
i.e. fix it for Chrome's sake
2018-01-11 12:32:31 -06:00
Lance Edgar 935752c786 Update changelog 2018-01-11 12:26:38 -06:00
Lance Edgar e2cdb4387a Fix row query bug when deleting batch row 2018-01-11 12:25:25 -06:00
Lance Edgar 3dce9d9ed3 Fix dependencies when building docs via tox 2018-01-11 12:17:21 -06:00
Lance Edgar 99ed897bf4 Add getting started doc for dev environment
also change docs theme
2018-01-11 12:15:37 -06:00
Lance Edgar c750ea2355 Tweak feedback dialog styles a bit 2018-01-10 21:23:58 -06:00
Lance Edgar e3ca3c9370 Fix readonly default renderers for association proxy fields 2018-01-10 21:18:38 -06:00
Lance Edgar bfa398bee1 Provide some default readonly form field renderers 2018-01-10 20:46:49 -06:00
Lance Edgar 485c96fec1 Update changelog 2018-01-10 19:56:14 -06:00
Lance Edgar acb4a77032 Add first attempt at master3 for batch views 2018-01-08 22:49:45 -06:00
Lance Edgar 365a48110c Refactor all tempmon views to use master3 2018-01-08 20:57:22 -06:00
Lance Edgar ce0195bd51 Refactor several more straggler views to use master3 2018-01-08 20:41:31 -06:00
Lance Edgar 3097f46aa1 Refactor products view to use master3 2018-01-08 18:03:51 -06:00
Lance Edgar d9a5b4a0f5 Update changelog 2018-01-08 15:33:06 -06:00
Lance Edgar 8d35955d03 Fix bug when printing product label 2018-01-08 15:32:36 -06:00
Lance Edgar c00f7e2144 Update changelog 2018-01-08 12:49:21 -06:00
Lance Edgar 66d3b7b4af Tweak diff styles when viewing upgrade 2018-01-07 19:44:39 -06:00
Lance Edgar a68bf572cc Update changelog 2018-01-07 17:02:15 -06:00
Lance Edgar fb140f24c1 Add basic UI support for "importer batch" feature 2018-01-06 20:28:59 -06:00
Lance Edgar c3c77ed586 Tweak diffs.css and refactor 'view_version' template to use it 2018-01-06 20:28:13 -06:00
Lance Edgar 568a625500 Add row_title to template context for view_row 2018-01-06 20:26:57 -06:00
Lance Edgar 85e6e7e08a Refactor away the row_route_prefix concept 2018-01-06 20:25:55 -06:00
Lance Edgar 22b8643def Add basic support for row grid view links 2018-01-06 20:02:51 -06:00
Lance Edgar b2020686f5 Tweak conditions for CSV row download link 2018-01-06 19:27:37 -06:00
Lance Edgar aa4051a7cd Exclude JS for refreshing batch unless it's relevant 2018-01-06 19:26:31 -06:00
Lance Edgar 50d6f1f95a Let grids be paginated even when they have no model class 2018-01-06 19:21:45 -06:00
Lance Edgar 582aabc1a3 Add empty default when displaying values in grid 2018-01-06 19:20:55 -06:00
Lance Edgar 46d0e96321 Stop setting execution details when multiple batches executed
that's now the handler's job
2018-01-05 20:39:38 -06:00
Lance Edgar dd22c04573 Add view for InventoryAdjustmentReason model 2018-01-05 18:37:16 -06:00
Lance Edgar 067cd60e20 Change how select menus are enhanced for batch exec options
jquery selectmenu doesn't play nicely in a dialog, when expanded (options are
cut off from screen)
2018-01-05 17:13:01 -06:00
Lance Edgar e78777f8e1 Add field name as wrapper class name 2018-01-05 17:12:42 -06:00
Lance Edgar ec0865b03f Show 'static_prices' flag for label batches 2018-01-05 16:52:08 -06:00
Lance Edgar 6ed37743a5 Fix some styles for execution options dialog 2018-01-05 11:21:30 -06:00
Lance Edgar 1767cef701 Update changelog 2018-01-05 09:41:58 -06:00
Lance Edgar b2fe300f02 Fix bug when making batch from product query 2018-01-05 09:36:02 -06:00
Lance Edgar 061aa889a6 Update changelog 2018-01-04 15:46:24 -06:00
Lance Edgar 80903bde38 Refactor forms logic when making batch from product query
use colander/deform instead of wtforms.  also make sure param names are unique
per batch type, within form controls
2018-01-04 15:08:03 -06:00
Lance Edgar 2cc0bb1995 Tweak product filter for report code name
i.e. make it more clear that it leverages the name
2018-01-04 10:38:15 -06:00
Lance Edgar 8d3846b2f2 Show row count field when viewing vendor catalog batch 2017-12-21 20:44:10 -06:00
Lance Edgar e58ca10e25 Make jQuery time widget input even more flexible
e.g. allow any of:

* 01:30 PM
* 1:30pm
* 11 AM
* 11am
2017-12-20 21:36:11 -06:00
Lance Edgar 42b97d1e1a Add a bit more flexibility to jquery time input values
i.e. for when the user hand-keys a value
2017-12-20 21:30:14 -06:00
Lance Edgar 9f14d01c22 Add "price required" flag to product view 2017-12-20 19:02:03 -06:00
Lance Edgar c0cb3d70ff Update changelog 2017-12-20 17:36:13 -06:00
Lance Edgar 163c8945ed Provide sane width for filter value dropdowns 2017-12-20 14:25:10 -06:00
Lance Edgar 36aaa4d70c Update changelog 2017-12-19 20:48:49 -06:00
Lance Edgar f17617c659 Accept value_enum kwarg when creating grid filter
and do the "normal" thing for that if one is given
2017-12-19 14:21:19 -06:00
Lance Edgar 873104d573 Update changelog 2017-12-08 18:04:46 -06:00
Lance Edgar 927eb3b38c Add custom schema type for jQuery time picker data 2017-12-08 17:38:52 -06:00
Lance Edgar abd47ae7ae Fix deserialize logic for jQuery time-picker widget 2017-12-08 14:39:23 -06:00
Lance Edgar 1e8a4534d5 Various forms2 changes 2017-12-07 20:35:44 -06:00
Lance Edgar 587871e87c Add Grid.remove_filter() method 2017-12-06 19:54:13 -06:00
Lance Edgar 908ca52b08 Fix type for export 'created' field 2017-12-06 14:59:11 -06:00
Lance Edgar ef720e3a59 Refactor reports view to use master3 2017-12-06 13:50:02 -06:00
Lance Edgar 09ba419ee3 Refactor "exports" views to use master3 2017-12-06 13:40:00 -06:00
Lance Edgar 86cfc59d33 Refactor user and role views to use master3 2017-12-06 12:40:27 -06:00
Lance Edgar 789bdef190 Add cleared/selected callbacks for jquery autocomplete in forms2 2017-12-05 20:36:57 -06:00
Lance Edgar bb12c5107c Refactor purchases view to use master3 2017-12-05 18:52:54 -06:00
Lance Edgar 8041c085f6 Add basic "helptext" support for forms2 2017-12-05 15:07:25 -06:00
Lance Edgar c20fdf4450 Change template prefix for vendor catalog batches 2017-12-05 13:33:05 -06:00
Lance Edgar 4902fab187 Refactor views to use Grid.set_sort_defaults() method 2017-12-04 22:40:10 -06:00
Lance Edgar 7d79727c2e Refactor vendors view to use master3 2017-12-04 22:02:46 -06:00
Lance Edgar dfba168504 Whoops, make shifts view really use master3 2017-12-04 21:11:27 -06:00
Lance Edgar 2762230691 Refactor raw shifts view to use master3 2017-12-04 21:09:44 -06:00
Lance Edgar 20bae8e54b Add Grid.set_sort_defaults() method 2017-12-04 20:53:46 -06:00
Lance Edgar 332fadd42e Refactor people view to use master3 2017-12-04 18:49:52 -06:00
Lance Edgar c3fb86e391 Refactor messages view to use master3 2017-12-04 17:52:25 -06:00
Lance Edgar 84ebf5d929 Refactor employees view to use master3 2017-12-04 13:48:31 -06:00
Lance Edgar 7a777964a7 Add transaction "System ID" field for Trainwreck 2017-12-03 20:33:25 -06:00
Lance Edgar 984072467e Update changelog 2017-12-03 19:49:34 -06:00
Lance Edgar b793998814 Expose default address for customers view 2017-12-03 12:23:43 -06:00
Lance Edgar 6da013bf6c Fix permission bug for executing multiple batch results 2017-12-02 18:14:31 -06:00
Lance Edgar 16eeb501ca Fix permission bug for mobile inventory batch 2017-12-02 18:10:33 -06:00
Lance Edgar 64afab821f Allow "execute results" for inventory batches 2017-12-02 17:59:21 -06:00
Lance Edgar d27759ac2d Fix bug? or maybe there's no point 2017-12-02 17:20:40 -06:00
Lance Edgar a7d8cfcdbb Let batch views allow or deny "execute results" option 2017-12-02 17:08:17 -06:00
Lance Edgar 277d98ae2c Tweak template prefix for label batch views 2017-12-02 16:25:59 -06:00
Lance Edgar 70a34615a3 Expose description and notes for label batches 2017-12-02 14:20:02 -06:00
Lance Edgar f06fff983e Allow bulk delete of label batch rows 2017-12-02 13:24:52 -06:00
Lance Edgar 11a63ab2ef Fix batch row count when bulk-deleting rows 2017-12-02 13:24:38 -06:00
Lance Edgar 9cf5c9385d Add batch description to page body title 2017-11-30 11:22:54 -06:00
Lance Edgar 6decabb369 Various batch tweaks, for better execution options etc. 2017-11-29 18:26:55 -06:00
Lance Edgar df3623b663 Add more "manually priced" awareness to pricing batch UI 2017-11-29 18:25:47 -06:00
Lance Edgar 366b1c9073 Hide status when creating new purchasing batch 2017-11-28 09:44:30 -06:00
Lance Edgar ac733ae6ea Various tweaks for sake of forms2 refactor 2017-11-27 12:10:17 -06:00
Lance Edgar 43ce0fb44f Auto-scroll window as needed to ensure drop-down choices are visible 2017-11-22 11:21:59 -06:00
Lance Edgar 40d2251844 Add custom FieldList class for forms2 field list 2017-11-22 11:21:28 -06:00
Lance Edgar 4c189f2fcc Rather shoddy refactor of customers view to use master3
seems to work well enough for now..
2017-11-21 20:46:49 -06:00
Lance Edgar 3d7acbbe1d Update changelog 2017-11-21 14:06:02 -06:00
Lance Edgar 9c205d7da5 Add colander magic for association proxy fields
hopefully now any association proxy fields which are included, will be given
the appropriate type and widget.  however this still doesn't work for the
readonly rendering of fields...
2017-11-21 11:11:18 -06:00
Lance Edgar 6ea88808b2 Add date/time-picker, autocomplete support for forms2 (deform) 2017-11-20 17:01:08 -06:00
Lance Edgar f541a94351 Set widget when defining enum for a form2 field 2017-11-20 12:38:50 -06:00
Lance Edgar 1325e507fb Update changelog 2017-11-19 17:45:40 -06:00
Lance Edgar 3861d46ce3 Fix (hack) for editing some department flags
not sure why this is necessary, but not very important for now...
2017-11-18 22:34:43 -06:00
Lance Edgar a8c8447297 Improve auto-disable logic for some form buttons 2017-11-17 17:27:33 -06:00
Lance Edgar 455f991857 Update changelog 2017-11-11 11:43:27 -06:00
Lance Edgar 2cba0ade84 Accept None as valid arg for Grid.set_filter()
i.e. to effectively remove the filter
2017-11-11 09:52:11 -06:00
Lance Edgar 152db68606 Install rattail[auth] dependencies for tox builds 2017-11-11 08:15:45 -06:00
Lance Edgar dae827a45b Update changelog 2017-11-08 13:03:36 -06:00
Lance Edgar d874fccfd1 Fix manifest to include *.pt deform templates
i think these are chameleon templates?
2017-11-08 13:02:37 -06:00
Lance Edgar 04735dfb4f Update changelog 2017-11-08 12:24:01 -06:00
Lance Edgar 46293546b6 Add json to global template context
just seems like a useful thing to have around...
2017-11-02 21:38:16 -07:00
Lance Edgar 7ccb38b5ab Update changelog 2017-11-01 21:04:28 -07:00
Lance Edgar 5f04ac9cb4 Add "text" type for new form fields 2017-10-31 13:25:19 -07:00
Lance Edgar c7855f2ca5 Add description, notes for pricing batches 2017-10-30 21:33:25 -07:00
Lance Edgar aea4379fe4 Add sorters, filters for Product regular, current price 2017-10-30 21:23:00 -07:00
Lance Edgar c320ab2feb Add product and personnel flags for Department 2017-10-30 20:52:07 -07:00
Lance Edgar 3f335315ab Update changelog 2017-10-29 22:50:11 -07:00
Lance Edgar 17d0ee64c2 Fix join bug for Upgrades table when sorting by executor 2017-10-29 22:48:49 -07:00
Lance Edgar d89a21e2b0 Update changelog 2017-10-29 01:23:19 -07:00
Lance Edgar 2bbe6c8346 Add "make user" button when viewing person w/ no user account 2017-10-29 01:18:05 -07:00
Lance Edgar b26036f366 Update changelog 2017-10-28 16:45:13 -07:00
Lance Edgar d67350b93b Add cashier info, upload time for Trainwreck transaction views 2017-10-28 11:30:53 -07:00
Lance Edgar f11210fa2b Update changelog 2017-10-25 23:11:09 -07:00
Lance Edgar ff7fd94b57 Use master3 view for datasync changes 2017-10-24 21:21:43 -07:00
Lance Edgar 1ee822d715 Add support for validator and required flag, for new forms 2017-10-24 19:58:59 -07:00
Lance Edgar 8bcb2f750a Update changelog 2017-10-24 10:18:42 -07:00
Lance Edgar 2bd2839107 Export Person.users relationship (readonly) 2017-10-24 10:09:28 -07:00
Lance Edgar a51d4e54db Add item_id to trainwreck views
er, add scancode, since item_id was renamed..
2017-10-24 09:51:18 -07:00
Lance Edgar 52342a7612 Fix value auto-selection for enum grid filters 2017-10-22 20:56:30 -07:00
Lance Edgar 401cba23b7 Add grid filter which treats empty string as NULL 2017-10-22 20:00:31 -07:00
Lance Edgar 512405f01f Fix bug in MasterView.get_effective_row_query()
really this method should be removed...
2017-10-22 12:55:41 -07:00
Lance Edgar 8c599f368e Update changelog 2017-10-20 11:51:48 -07:00
Lance Edgar 855153f121 Fix bug with products view config 2017-10-20 11:51:07 -07:00
Lance Edgar cddb05d8fc Update changelog 2017-10-19 08:48:34 -07:00
Lance Edgar 14f67746bf Allow passing None to Grid.set_joiner()
doing so will remove the joiner
2017-10-18 12:54:57 -07:00
Lance Edgar f8e98a5817 Avoid potential bugs when generating CSV results data for download 2017-10-16 17:38:42 -07:00
Lance Edgar 46b49ab987 Add 'currency' field type for new forms 2017-10-14 18:52:30 -07:00
Lance Edgar 7442b933fd Add support for setting default field values on new forms
i.e. those using Colander schema
2017-10-14 17:55:29 -07:00
Lance Edgar c95e2dbb06 Add "download row results as CSV" feature to master view 2017-10-14 14:14:24 -07:00
Lance Edgar f338a03c97 Add 'active' column to Users table view 2017-10-13 14:12:28 -07:00
Lance Edgar 8d002f76d2 Convert user feedback mechanism to use modal dialog
instead of navigating to new page.  this is how it should have been done
to begin with...
2017-10-13 08:01:43 -07:00
Lance Edgar 827cc592b4 Make CSRF protection optional (but on by default) 2017-10-11 15:57:34 -07:00
Lance Edgar 6281593084 Add "local" datetime renderer for new grids, forms 2017-10-10 13:58:52 -07:00
Lance Edgar 791f3beffc Update changelog 2017-09-28 12:13:30 -05:00
Lance Edgar 6186700a66 Add 'duration' type for new form fields
this only supports readonly, for now..
2017-09-26 22:06:50 -05:00
Lance Edgar 47ce0fd448 Add "populatable" for master views (populating new objects with progress) 2017-09-23 16:27:22 -05:00
Lance Edgar 8a945f8baf Pass form along to before_create_flush() in master3 2017-09-23 16:26:56 -05:00
Lance Edgar e283288a26 Fix deform widget resource inclusion for master/create template 2017-09-23 16:26:22 -05:00
Lance Edgar 52747ea6bd Auto-enhance all select widget fields for deform 2017-09-23 16:26:03 -05:00
Lance Edgar 7cb4664018 Add ability to override schema node for custom deform fields 2017-09-23 16:25:37 -05:00
Lance Edgar 3361adf08a Copy select field template from deform
this is before any modifications, straight from deform
2017-09-23 14:12:27 -05:00
Lance Edgar 3bcec30a4c Don't set batch input file on creation, if no file exists 2017-09-23 13:32:12 -05:00
Lance Edgar e1384c2ab1 Tweak default labels for created(by) batch form fields 2017-09-23 11:48:38 -05:00
Lance Edgar 8d78fad621 Fix data type/size issue with CSV download 2017-09-16 18:42:18 -05:00
Lance Edgar c497604a30 Update changelog 2017-09-15 21:03:15 -05:00
Lance Edgar 3564ab0e1c Tweak title for master view row template 2017-09-15 21:02:13 -05:00
Lance Edgar 9ff6df83e5 Add generic support for downloading list results as CSV 2017-09-14 21:57:37 -05:00
Lance Edgar f6d9f7a913 Fix user field rendering when no person associated 2017-09-03 13:00:44 -05:00
Lance Edgar b76f568d7d Update changelog 2017-08-30 20:09:34 -05:00
Lance Edgar 45d4329630 Log debug instead of error when package diff render fails
we probably don't want email noise about this..
2017-08-20 13:12:55 -05:00
Lance Edgar f74c93e3e7 Fix some bugs for rendering upgrade package diffs
in particular, when a new package gets installed and there is no "old"
version for it
2017-08-20 13:03:30 -05:00
Lance Edgar ee606275ad Update changelog 2017-08-18 11:23:05 -05:00
Lance Edgar 2448d71edd Convert customer groups view to master3 2017-08-17 20:28:54 -05:00
Lance Edgar 7dbdaf1f8a Convert categories view to master3 2017-08-17 20:25:21 -05:00
Lance Edgar 34bc59f96f Convert brands view to master3 2017-08-17 20:20:15 -05:00
Lance Edgar e3de40bdfe Convert bouncer view to master3
also move common file field logic from upgrades into master3
2017-08-17 20:13:42 -05:00
Lance Edgar 2db9d31386 Use shared logic for executing upgrade
now that it's part of the handler
2017-08-17 18:16:28 -05:00
Lance Edgar 639644375d Give the "More" link a but of extra space from other actions 2017-08-17 18:00:22 -05:00
Lance Edgar c038d74302 Show all grid actions by default, if there are 3 or less 2017-08-17 17:25:18 -05:00
Lance Edgar 63e336d4bb Override deform template for checkbox field; fix label behavior 2017-08-17 17:21:07 -05:00
Lance Edgar 0d144ff58b Convert subdepartments view to master3 2017-08-17 00:45:11 -05:00
Lance Edgar bbfa15845a Convert departments view to master3 2017-08-17 00:45:08 -05:00
Lance Edgar 3477637c74 Allow batch execution to require options on a per-batch basis
plus some other changes i think..
2017-08-16 23:27:27 -05:00
Lance Edgar 98ca378302 Add extra perms for creating inventory batch w/ different modes 2017-08-16 22:26:52 -05:00
Lance Edgar 860f990a2e Fix mobile inventory template 2017-08-16 22:11:30 -05:00
Lance Edgar 422b7681f4 Update changelog 2017-08-16 20:46:00 -05:00
Lance Edgar e945ebe325 Fix auto-disable button on form submit, per Chrome issues
dang it chrome, why you gotta be like that
2017-08-16 19:10:39 -05:00
Lance Edgar 178b9f2bcb Add LocalDateTimeFieldRenderer for formalchemy 2017-08-16 18:32:17 -05:00
Lance Edgar d83990bb58 Update changelog 2017-08-15 16:52:27 -05:00
Lance Edgar 0469ddea7a Fix permission used for mobile receiving item lookup 2017-08-15 16:49:49 -05:00
Lance Edgar b309df005c Tweak how pyramid config is created during app startup, for tests
still not sure if this is quite right, but seems to work for now
2017-08-15 16:13:35 -05:00
Lance Edgar ec4e52fa1a Add mechanism for user to bulk-change status for purchase credits
trying to stay pretty generic yet...
2017-08-15 12:58:16 -05:00
Lance Edgar 0516d44842 Let handler delete files when deleting upgrade 2017-08-14 01:05:21 -05:00
Lance Edgar 56910ea4c1 Add generic changelog link for rattail/tailbone packages
i.e. production changelog, when not running from src
2017-08-13 21:23:11 -05:00
Lance Edgar cbf3a9e939 Update changelog 2017-08-13 20:36:14 -05:00
Lance Edgar 4639d4a7db Give older/newer buttons the autodisable treatment 2017-08-13 20:07:37 -05:00
Lance Edgar 0e42efd32b Fix core styles/javascript for base template 2017-08-13 19:14:54 -05:00
Lance Edgar 7d0bb80a90 Merge 'better' theme into base templates
i.e. for now there is no 'better' (or any other) theme
2017-08-13 19:11:53 -05:00
Lance Edgar c0a28716f5 Add prev/next buttons when viewing upgrade details 2017-08-13 18:28:40 -05:00
Lance Edgar 852bafdfa0 Improve logic for generating changelog links for upgrade package diffs 2017-08-13 16:30:31 -05:00
Lance Edgar 55f96c4730 Add initial support for changelog links for upgrade package diffs
definitely still just playing around so far...
2017-08-13 00:25:32 -05:00
Lance Edgar 4360d263e4 Test commit 2017-08-12 22:47:11 -05:00
Lance Edgar 4b5e415147 Add show all vs. show diffs for upgrade packages
plus some related tweaks
2017-08-12 22:38:23 -05:00
Lance Edgar 24d89db025 Update changelog 2017-08-12 20:40:58 -05:00
Lance Edgar bf09071e1d Make product field renderer allow override of link text rendering 2017-08-12 20:26:11 -05:00
Lance Edgar d51b9d2ad7 Update changelog 2017-08-11 11:55:24 -05:00
Lance Edgar 41e09271a2 Flush session once every 1000 records when bulk-deleting 2017-08-10 16:32:03 -05:00
Lance Edgar 46a43981df Fix join bug for users grid 2017-08-10 15:32:04 -05:00
Lance Edgar 0ad2113b81 Various tweaks to inventory batch logic
really to support zero-all mode, but several generic changes too
2017-08-10 11:10:42 -05:00
Lance Edgar 1d9489169b Update changelog 2017-08-09 23:16:13 -05:00
Lance Edgar 4f2bf5431d Fix clone config bug for label batches 2017-08-09 23:15:38 -05:00
Lance Edgar 3c3300a541 Update changelog 2017-08-09 22:53:11 -05:00
Lance Edgar a3e7556a06 Fix encoding bug when reading stdout during upgrade 2017-08-09 22:34:03 -05:00
Lance Edgar 18f4b4ff5c Various changes to support a certain new app
improve inventory support, plus "hiding" person data but still using it
2017-08-09 21:41:42 -05:00
Lance Edgar fcffe0f79d Update changelog 2017-08-09 14:16:53 -05:00
Lance Edgar 773a0c769d Fix upgrade stdout handling if file doesn't exist yet
plus some other tweaks..
2017-08-09 11:56:25 -05:00
Lance Edgar e5b0fe7198 Add running display of stdout.log when executing upgrade 2017-08-09 11:44:31 -05:00
Lance Edgar fbd73a48c4 Fix status when cloning upgrade 2017-08-08 21:43:04 -05:00
Lance Edgar d7f5211fc4 Add support for cloning an upgrade record
until this is all ironed out, seems like it will often be helpful
2017-08-08 21:26:31 -05:00
Lance Edgar 77880abb87 Add awareness of upgrade exit code, success/fail 2017-08-08 20:32:17 -05:00
Lance Edgar 7a14b42345 Update changelog 2017-08-08 19:41:38 -05:00
Lance Edgar 33a9516042 Specify expire_on_commit for tailbone db session
is this right..?  seems to be necessary for login now, in some
cases.. which surely doesn't make sense
2017-08-08 19:38:54 -05:00
Lance Edgar c40a993273 Update changelog 2017-08-08 18:00:58 -05:00
Lance Edgar b28dc0702e Fix bug which caused new empty worked shift when editing time sheet 2017-08-08 17:59:57 -05:00
Lance Edgar 158755377b Update changelog 2017-08-08 17:06:02 -05:00
Lance Edgar e80f8b31c1 Fix numeric filter to allow 3 decimal places by default 2017-08-08 17:04:59 -05:00
Lance Edgar 4101e056e4 Fix permission check for deleting single batch rows 2017-08-08 17:00:38 -05:00
Lance Edgar 2dc539c357 Fix bulk-delete for batch rows, allow it for pricing batches 2017-08-08 16:57:05 -05:00
Lance Edgar 2df51bfef8 Update changelog 2017-08-08 14:44:42 -05:00
Lance Edgar 820841d4e0 Remove unwanted import (which broke versioning)
ugh, now there's a check on startup to hopefully prevent this sort of
thing from sneaking up on us again
2017-08-08 14:41:52 -05:00
Lance Edgar e91f18f344 Add some links to employees grid 2017-08-08 13:02:57 -05:00
Lance Edgar 9335381560 Update changelog 2017-08-08 00:50:20 -05:00
Lance Edgar 3fcc105b78 Only use monospace fonts in diff table if so specified 2017-08-07 23:23:58 -05:00
Lance Edgar 2714d3c03c Tweak logging when object fails to be executed 2017-08-07 23:07:36 -05:00
Lance Edgar e14b5a89c3 Improve status tracking for upgrades; add package version diff 2017-08-07 22:23:07 -05:00
Lance Edgar 430a1416c6 Fix recipients renderer for email settings grid 2017-08-07 19:09:03 -05:00
Lance Edgar 4cb4d9b14c Stop trying to persist session used for upgrade execution progress
apparently that trick won't work as long as we're waiting in-process
for the upgrade process to complete..
2017-08-07 18:50:50 -05:00
Lance Edgar f46e20c119 Refactor progress bars somewhat to allow file-based sessions
hoping this solves issue of Apache restart at end of upgrade
2017-08-07 18:19:29 -05:00
Lance Edgar f203f2c377 Update changelog 2017-08-07 14:38:09 -05:00
Lance Edgar f5688f1f90 Add basic support for performing / tracking app upgrades
also add `MasterView.executable` and friends
2017-08-05 22:07:49 -05:00
Lance Edgar f476c696fd Make datasync changes bulk-deletable 2017-08-05 16:12:06 -05:00
Lance Edgar 941ce1a9cb Record become/stop root user events 2017-08-05 16:11:56 -05:00
Lance Edgar 54a364aa0c Update changelog 2017-08-04 18:18:08 -05:00
Lance Edgar 2f0f3fa463 Expose UserEvent table in UI
normal table access, plus per-user row grid
2017-08-04 17:14:38 -05:00
Lance Edgar 82e8f49dd1 Record basic user login/logout events 2017-08-04 16:48:33 -05:00
Lance Edgar eaa47dbd8a Add rattail-tempmon dependency for tox tests 2017-08-04 16:20:18 -05:00
Lance Edgar ba877eb3e9 Update changelog 2017-08-04 16:13:08 -05:00
Lance Edgar 3205d61ba6 Add progress support for bulk deletion
plus bulk-delete all tempmon readings when deleting client or probe
2017-08-04 16:11:45 -05:00
Lance Edgar d8be651e95 Make tempmon readings bulk-deletable
although if there are enough of them, it can still suck..  need to add a
progress bar for bulk-delete at some point..
2017-08-04 15:15:43 -05:00
Lance Edgar f4d4dcbdd2 Update changelog 2017-08-04 12:00:12 -05:00
Lance Edgar dce0efb5fa Various view tweaks 2017-08-04 11:55:53 -05:00
Lance Edgar 97fb74f093 Update changelog 2017-08-04 10:06:43 -05:00
Lance Edgar b4cabadcd9 Fix row highlighting for sources panel on product view 2017-08-04 10:05:43 -05:00
Lance Edgar ea7eb47551 Add auto-links for most grids
probably still missing some yet?
2017-08-03 19:16:53 -05:00
Lance Edgar bd3d948bf4 Update changelog 2017-08-03 17:11:05 -05:00
Lance Edgar d1aaac5b16 Don't assume all rows belong to a batch
whooops..
2017-08-03 17:06:26 -05:00
Lance Edgar f20a40e818 Add some links to various grid columns 2017-08-02 23:31:08 -05:00
Lance Edgar 8186366b69 Add view for consuming new batch ID; misc. tweaks for grids etc. 2017-08-02 19:16:45 -05:00
Lance Edgar 6ae129ea24 Fix bug when request.user becomes unattached from session (?)
this sure seems unexpected.  so far the behavior has only been seen on
mobile when a new ordering batch was created
2017-08-02 13:18:19 -05:00
Lance Edgar 961249722f Some tweaks to ordering batch views 2017-08-02 13:18:05 -05:00
Lance Edgar 65c63dad3e Initial support for mobile ordering
plus various other changes required for that
2017-08-02 12:08:23 -05:00
Lance Edgar 5afa832684 Add 'data-uuid' attr for mobile grid list items, if applicable 2017-08-02 12:04:32 -05:00
Lance Edgar 09ffdba9ef Allow product field renderer to suppress hyperlink 2017-08-02 12:04:03 -05:00
Lance Edgar b160ac64eb Update changelog 2017-08-01 15:05:34 -05:00
Lance Edgar 93fa361292 Add "on order" count to products grid, tweak product notes panel 2017-08-01 14:54:04 -05:00
Lance Edgar 3820891277 Fix batch links when viewing purchase object 2017-08-01 14:54:04 -05:00
Lance Edgar 0171f3ebba Various improvements to batch worksheets, index links etc. 2017-08-01 14:53:45 -05:00
Lance Edgar 00027b09f6 Fix styles for message compose template 2017-08-01 14:39:38 -05:00
Lance Edgar cbf4ca8479 Improve verbiage for exception view
suggest the user submit Feedback to be notified of bugfix etc.
2017-08-01 14:38:53 -05:00
Lance Edgar d93cb4f07b Fix how we detect grid settings presence in user session
..in case grid has filter settings only
2017-08-01 14:38:09 -05:00
Lance Edgar 5b35c3dd3b Make login template use same logo as home page 2017-07-31 13:58:38 -05:00
Lance Edgar 94894b2d27 Update changelog 2017-07-26 17:11:35 -05:00
Lance Edgar 39cf32bb0a Allow master view to decide whether each grid checkbox is checked
aka. un-break what the v3 grids broke..
2017-07-26 17:10:44 -05:00
Lance Edgar f1bb603f93 Update changelog 2017-07-26 15:41:39 -05:00
Lance Edgar c82c55942f Stop checking for pre-0.7 SQLAlchemy 2017-07-19 03:15:10 -05:00
Lance Edgar d3bc1abb57 Add some more support for product inventory and status 2017-07-19 03:08:32 -05:00
Lance Edgar e4b2cd638a Stop allowing pre-0.7 SQLAlchemy
some recent version broke tests, let's just skip this check
2017-07-19 01:44:42 -05:00
Lance Edgar 61d504afb8 Various tweaks for support of native inventory
certaianly some other things made it in here too..
2017-07-19 01:42:18 -05:00
Lance Edgar eb68eec520 Update changelog 2017-07-18 17:52:53 -05:00
Lance Edgar f9906b26f2 Tweak some basic styles for forms/grids 2017-07-18 16:51:15 -05:00
Lance Edgar 66a3d6e66e Add colander/deform dependencies 2017-07-18 16:23:46 -05:00
Lance Edgar 4dcd89fba7 Add new v3 master with v2 forms, with colander/deform
goal here is to replace FormAlchemy dependency, slowly but surely..
so far only the Settings and Stores views use v3 master
2017-07-18 16:17:00 -05:00
Lance Edgar 2b5aaa0753 Update changelog 2017-07-18 14:03:22 -05:00
Lance Edgar cb6b093a2a Fix grid bug if "current page" becomes invalid
sometimes it was possible to fall outside the valid page range, in which
case grid would stop showing results!
2017-07-18 13:26:17 -05:00
Lance Edgar 69778a4682 Fix import bug 2017-07-18 13:26:04 -05:00
Lance Edgar 915929b500 Update changelog 2017-07-15 02:48:50 -05:00
Lance Edgar bea28e97e9 Expose version history for all supported tables
mostly for sake of products, but various..
2017-07-15 02:46:39 -05:00
Lance Edgar 5be3671a77 Update changelog 2017-07-14 23:57:01 -05:00
Lance Edgar 965dac9f43 Refactor (coalesce) all batch-related templates 2017-07-14 22:08:48 -05:00
Lance Edgar 951057d8c2 Refactor / cleanup v2 batch master
also remove old/unused logic from v1 batch master
2017-07-14 21:55:35 -05:00
Lance Edgar df1c0b0b5e Fix grid pager styles; add default config for version grids
also remove all old grid code from v1 MasterView, since it now is
entirely superseded by v2 master
2017-07-14 21:50:07 -05:00
Lance Edgar 55ca7d5117 Remove references to master2 templates 2017-07-14 21:38:46 -05:00
Lance Edgar a18f55854d Refactor master2/index => master/index template 2017-07-14 21:23:40 -05:00
Lance Edgar 292546e44b Final grid refactor for all templates and CSS/JS (newgrid -> grid) 2017-07-14 21:15:22 -05:00
Lance Edgar c57e2e17cc Final grid refactor; we now have just 'grids' :)
this also removes some old UI stuff for the first attempt at continuum
versioning..among other cruft
2017-07-14 20:30:00 -05:00
Lance Edgar 52c7f485ab Remove some more references to 'newgrids' / old MasterView 2017-07-14 19:20:19 -05:00
Lance Edgar 172efe2ab9 Add flexible grid class for v3 grids for width=half etc.
also add 'percent' type renderer, and include column name in <td> class
2017-07-14 18:21:24 -05:00
Lance Edgar 38418a4200 Provide default renderers for SA mapped tables, where possible 2017-07-14 17:12:10 -05:00
Lance Edgar 6954748452 Update changelog 2017-07-14 16:45:24 -05:00
Lance Edgar 2a9cf3db49 Fix master view get_effective_data() for v3 grids
used when creating new batch from product results etc.

this also tweaks purchase credits view a bit
2017-07-14 16:43:49 -05:00
Lance Edgar 82f4a6efcd Update changelog 2017-07-14 16:04:50 -05:00
Lance Edgar 242d37c95d Fix bug for printing one-off product labels 2017-07-14 16:03:23 -05:00
Lance Edgar d090338ba5 Update changelog 2017-07-14 15:48:00 -05:00
Lance Edgar c18774e5e5 Fix template/styles for v3 grid views, add purchasing batch status 2017-07-14 15:45:31 -05:00
Lance Edgar c774d6c8e3 Update changelog 2017-07-14 14:41:21 -05:00
Lance Edgar 5b1ae27a10 Add new "v3" grids, refactor all views to use them
or at least that's the idea..hopefully we caught them all
2017-07-14 03:57:36 -05:00
Lance Edgar f244c2934b Update changelog 2017-07-13 18:12:39 -05:00
Lance Edgar 270b33de27 Make background color white, for merge diff table 2017-07-11 23:58:36 -05:00
Lance Edgar 4e7837baa9 Tweak (fix) color highlight for version diff view 2017-07-11 23:56:02 -05:00
Lance Edgar 5bff7d16b9 Tweak wording for master merge template 2017-07-11 23:23:23 -05:00
Lance Edgar 1685d37c01 Fix typo 2017-07-11 23:20:51 -05:00
Lance Edgar 8636db2a53 Add unit cost for inventory batches 2017-07-11 22:40:53 -05:00
Lance Edgar 1791bd745b More tweaks to support mobile inventory batches 2017-07-11 21:59:12 -05:00
Lance Edgar 8a5dbc33a7 Refactor keypad widget for mobile receiving
logic for this is now held in common
2017-07-11 21:05:26 -05:00
Lance Edgar 32d256932e Various tweaks to support mobile inventory batches
still not fully there I think, but pretty close..
2017-07-11 20:57:52 -05:00
Lance Edgar 452cb99349 Add basic support for viewing inventory batches on mobile 2017-07-11 16:23:11 -05:00
Lance Edgar 60104f05c7 Make all batches support mobile by default
with default pending/etc. filter also
2017-07-11 15:25:05 -05:00
Lance Edgar 48f5da4511 Add global key handler for mobile receiving, for scanner wedge input
this way we don't have to focus the UPC search box, since that seems to
always popup the device keyboard.
2017-07-11 13:05:35 -05:00
Lance Edgar bf3d7b9143 Stop using popup for expiration date, for mobile receiving
that was causing event headaches..this way is simpler
2017-07-11 12:13:19 -05:00
Lance Edgar dda79a491f Fix bug with mobile receiving UPC lookup; require stronger perm
doing a UPC lookup for mobile receiving now requires "create batch row"
permissions, since the view is capable of just that
2017-07-11 11:15:26 -05:00
Lance Edgar 148cbd2f57 Fix bug with "memory" of mobile grid filters 2017-07-11 10:57:35 -05:00
Lance Edgar 72b2510681 Prevent mobile receiving actions for batch which is complete or executed 2017-07-11 10:45:05 -05:00
Lance Edgar af0eea76e2 Add logic for mobile receiving if product not in batch and/or system 2017-07-11 00:59:51 -05:00
Lance Edgar 7bbdf38551 Disable unused Clear button for mobile receiving
its purpose was unclear..go figure
2017-07-10 22:41:52 -05:00
Lance Edgar 98ff71a2dd Add filter support for mobile row grid; plus mark receiving as complete 2017-07-10 22:10:27 -05:00
Lance Edgar f47157102c Add initial/basic support for "simple" mobile grid filter w/ radio buttons
so far only one such filter is (presumably?) supported..etc.
2017-07-10 21:04:56 -05:00
Lance Edgar 9da7ba21bf Sort mobile receiving batches by ID desc 2017-07-10 16:39:35 -05:00
Lance Edgar 27c70eb459 Update changelog 2017-07-10 16:15:48 -05:00
Lance Edgar 85d18a760b Fix CS/EA bug for mobile receiving 2017-07-10 16:14:55 -05:00
Lance Edgar 5d9210085f Update changelog 2017-07-07 09:23:44 -05:00
Lance Edgar f0d177e5df Fix broken product image tag, per webhelpers2
this still probably needs more attention but this will do for now
2017-07-07 09:21:10 -05:00
Lance Edgar 21476d8173 Fix encoding bugs 2017-07-07 00:52:04 -05:00
Lance Edgar 581ced2990 Switch license to GPL v3 (no longer Affero)
refs #2
2017-07-06 23:47:56 -05:00
Lance Edgar 602180c45b Update changelog 2017-07-06 21:31:01 -05:00
Lance Edgar 631665e208 Refactor all remaining usage of webhelpers; use webhelpers2 etc. instead 2017-07-06 21:11:05 -05:00
Lance Edgar 018702159d Tweak how options are created for enum/choice filter value fields 2017-07-06 21:01:23 -05:00
Lance Edgar 6302d5a351 Refactor grids to use new 'paginate' library
instead of the older `webhelpers.paginate`
2017-07-06 20:57:53 -05:00
Lance Edgar 8014e60d14 Add webhelpers2 dependency, use it for most things
still using webhelpers for certain things yet, hopefully can get rid of that soon
2017-07-06 20:13:42 -05:00
Lance Edgar 5bded89466 Replace occurrences of execfile() 2017-07-06 16:25:29 -05:00
Lance Edgar 91a14c81a9 Remove all old-style CRUD views 2017-07-06 15:46:05 -05:00
Lance Edgar 53d69acbcc Remove all old-style batch CRUD views 2017-07-06 15:44:37 -05:00
Lance Edgar 62fa0f9fcb Remove all "old-style" (aka. version 1) grids 2017-07-06 15:23:33 -05:00
Lance Edgar 0befc46070 Refactor "departments by vendor" grid to use newer-style 2017-07-06 14:34:35 -05:00
Lance Edgar 3b97757d7f Remove unused reference to deprecated view 2017-07-06 01:00:26 -05:00
Lance Edgar d1436e4fd3 Revert "Remove all "old-style" (aka. version 1) grids"
This reverts commit 0085e2ccc4.

hm dang, sure enough broke stuff
2017-07-06 00:31:16 -05:00
Lance Edgar 0085e2ccc4 Remove all "old-style" (aka. version 1) grids
hopefully this doesn't break anything! ;)
2017-07-06 00:28:01 -05:00
Lance Edgar 59b170e745 Remove reference to old-style version view 2017-07-06 00:23:33 -05:00
Lance Edgar 66cc6cd39a Remove old-style continuum version views 2017-07-06 00:19:31 -05:00
Lance Edgar 83751f7c9e Fix background color for diff tables 2017-07-05 21:27:58 -05:00
Lance Edgar 85bdefc25b Add versioning display support for contact-related models 2017-07-05 17:16:28 -05:00
Lance Edgar 20ddf2687b Tweak header/title for versions listing page 2017-07-05 13:20:14 -05:00
Lance Edgar d9569882c9 Tweak config handling in case of running tests 2017-07-05 13:19:41 -05:00
Lance Edgar 0b68d56ddb Add basic versioning history support for master view
as with actual data versioning, we only support Person thus far
2017-07-05 03:07:35 -05:00
Lance Edgar 7340ef1f9b Rearrange some imports to ensure rattail.db.model comes last
this is necessary for Continuum versioning
2017-07-03 23:52:48 -05:00
Lance Edgar db0eaf8eb0 Make Person.employee field readonly 2017-07-03 23:52:30 -05:00
Lance Edgar a03083efdd Add initial support for expiration date for mobile receiving 2017-07-03 21:07:57 -05:00
Lance Edgar 4aa91414a5 Tweak how customer/person relationships are displayed
expose just a little more to make it easier to track down a data issue i had
2017-07-03 16:58:30 -05:00
Lance Edgar 24a2c15850 Make hyperlink optional for employee field renderer 2017-07-03 15:52:41 -05:00
Lance Edgar e2b22221c4 Add custom default grid row size for Trainwreck items
seems like 100 rows is way more useful for this one
2017-07-01 19:06:05 -05:00
Lance Edgar cbdbcb6df9 Update changelog 2017-06-22 13:41:03 -05:00
Lance Edgar 888c094fe3 Allow bulk row deletion for vendor catalog batches 2017-06-22 13:28:35 -05:00
Lance Edgar 318189b839 Hide "execute results" button for handheld batches, unless permissions 2017-06-22 02:42:14 -05:00
Lance Edgar 1ce2f410d0 Tweak device type list field when making new handheld batch
i.e. sort by display name, etc.
2017-06-22 02:35:41 -05:00
Lance Edgar b2f96f4217 Make case/unit quantities prettier within Inventory batch rows grid 2017-06-22 01:21:15 -05:00
Lance Edgar b9a96f306b Add row count to inventory and label batch views 2017-06-22 01:17:34 -05:00
Lance Edgar 17017adde8 Tweak behavior when executing handheld batch results
redirect to final batch when done, try to warn user a bit
2017-06-22 01:02:03 -05:00
Lance Edgar 0d448fe6c5 Fix batch row count when deleting a row 2017-06-22 00:52:41 -05:00
Lance Edgar 5a0fa20e03 Add way to execute multiple handheld batches at once 2017-06-21 17:29:06 -05:00
Lance Edgar a63f2e3623 Tweak display of inventory/label batches to reflect multiple handheld batches 2017-06-21 15:36:32 -05:00
Lance Edgar 1fc3133f8e Try to keep batch status updated; display it for handheld batches
seems a little hacky but hoping it's safe at least
2017-06-21 13:24:27 -05:00
Lance Edgar 83dbf405f6 Add row count as available column to batch header grids 2017-06-21 12:59:13 -05:00
Lance Edgar 0fb789fc2c Tweak default views for Trainwreck data 2017-06-16 16:38:05 -07:00
Lance Edgar b468bc4b2b Add AlchemyLocalDateTimeFilter 2017-06-16 16:37:40 -07:00
Lance Edgar 80d2912874 Add basic views for Trainwreck transactions
..but never try to configure them, custom app must do that
2017-06-06 18:25:04 -07:00
Lance Edgar 02c93dd505 Update changelog 2017-06-05 20:54:49 -07:00
Lance Edgar d727efa6a0 Always add key as class to grid column headers; allow literal label 2017-06-01 19:29:32 -05:00
Lance Edgar c8b8608dc7 Update changelog 2017-05-30 20:27:44 -05:00
Lance Edgar cc41c38d68 Fix bug when updating Order Form data, if row.po_total is None
not sure how/when that happens, but just fall back to $0
2017-05-30 20:25:37 -05:00
Lance Edgar 2d29174772 Remove all views etc. for old-style batches 2017-05-25 16:46:31 -05:00
Lance Edgar 9e5b43ca66 Update changelog 2017-05-25 14:53:34 -05:00
Lance Edgar 95418ab459 Remove "case quantity" field from Ordering Batch download as Excel file 2017-05-25 12:34:12 -05:00
Lance Edgar da0f5cb63c Fix bug where batch notes weren't saved upon creation 2017-05-25 12:20:37 -05:00
Lance Edgar 522aad5880 Add basic ability to download Ordering Batch as Excel spreadsheet 2017-05-24 21:13:18 -05:00
Lance Edgar b841ce664e Make 'notes' field use textarea renderer by default, for all batches 2017-05-24 20:11:53 -05:00
Lance Edgar 346bb48f9c Add highlight to active row within Order Form view 2017-05-24 19:41:51 -05:00
Lance Edgar 37a9153c4d Pad session timeout warning by 10 seconds, to account for drift 2017-05-24 18:38:33 -05:00
Lance Edgar cf8df76788 Add support for bulk-delete of Pricing Batches 2017-05-24 18:10:38 -05:00
Lance Edgar 38d623bcf9 Fix behavior of mobile receiving when first entering a quantity
i.e. replace default of '1' if user first presses a numeric key, but
always append to quantity after that
2017-05-24 00:17:06 -05:00
Lance Edgar 5eca2347d5 Add initial support for mobile receiving views 2017-05-24 00:04:56 -05:00
Lance Edgar d68bf6b012 Refactor "purchasing" batch views, split off "ordering"
remainder will be handled when the time comes..
2017-05-23 13:44:07 -05:00
Lance Edgar 4875c8ebdc Simplify page title display for mobile base template 2017-05-23 11:30:38 -05:00
Lance Edgar d93b91f491 Various tweaks for mobile receiving batches
this is temporary, soon will refactor all that again..
2017-05-22 13:13:44 -05:00
Lance Edgar 5cb3f15616 Add validation for unique name when creating new Setting 2017-05-19 10:42:05 -05:00
Lance Edgar 242bcd7603 Remove references to deprecated batch handler methods 2017-05-18 11:57:30 -05:00
Lance Edgar cb39ca7970 Populate data rows for new mobile purchasing batch, if applicable
i.e. if batch is meant for receiving an existing PO etc.
2017-05-18 11:48:28 -05:00
Lance Edgar f0feefc7e5 Tweak how purchase batch is created via mobile
to let custom apps override args etc.
2017-05-18 11:33:01 -05:00
Lance Edgar 9e0e21399b Fix subtle bug when identifying purchase batch row on order form update 2017-05-18 09:22:39 -05:00
Lance Edgar d04f80e4e0 Update changelog 2017-05-18 08:43:20 -05:00
Lance Edgar d6bc584831 Add convenience dialog_button() JS function 2017-05-17 14:02:00 -05:00
Lance Edgar 76e71d634e Fall back to 'pretty' hours display if config is invalid etc. 2017-05-16 14:00:13 -05:00
Lance Edgar 3111aad7cd Expose full-time flag and start date for employee view 2017-05-16 13:44:02 -05:00
Lance Edgar 1dda8a961a Let config cause time sheet hours to display as HH.HH for some users
default display is still HH:MM however
2017-05-15 17:51:24 -05:00
Lance Edgar d7160a0a38 Add daily hour totals when viewing or editing single employee time sheet 2017-05-15 17:34:47 -05:00
Lance Edgar d1e092d9d3 Cap our pyramid_tm version until we can upgrade to pyramid 1.9 2017-05-13 14:41:03 -05:00
Lance Edgar 5989091a9d Don't include 'tailbone.views.core' b/c it no longer provides anything 2017-05-13 14:28:20 -05:00
Lance Edgar 9460b41ec2 Add basic 'robots.txt' support to CommonView 2017-05-13 14:26:16 -05:00
Lance Edgar 737973f4fc Remove unused 'fake_error' view
has been superseded by CommonView.bogus_error
2017-05-13 14:10:00 -05:00
Lance Edgar f02d6d4b16 Add basic support for Trainwreck database connectivity 2017-05-12 23:14:30 -05:00
Lance Edgar 17f9e6d4a9 Remove customer view template
we shouldn't be doing anything special here, leave that up to derived app
2017-05-11 13:59:51 -05:00
Lance Edgar eb272bf7f9 Tweak some customer view/field rendering, to allow more customization 2017-05-11 13:54:23 -05:00
Lance Edgar 56695d0c20 Allow batch view to override execution failure message 2017-05-11 09:49:15 -05:00
Lance Edgar 02962fbf87 Update changelog 2017-05-05 17:52:34 -05:00
Lance Edgar 5f8702e4cf Remove lower version for Pyramid dependency, but restrict to pre-1.9
this may not be the best idea, but needed a hack for now..
2017-05-05 00:26:17 -05:00
Lance Edgar 9107a26b63 Add basic support for deletion speedbump for row data 2017-05-04 16:35:21 -05:00
Lance Edgar 72ae1191a0 Let a batch disallow bulk-deletion of its rows 2017-05-04 16:34:48 -05:00
Lance Edgar 2c14dce30d Add allowance for Escape key, in numeric.js 2017-05-04 16:33:34 -05:00
Lance Edgar 7f5dbc422d Update changelog 2017-04-18 18:51:08 -05:00
Lance Edgar ee2137e1bf Add simple flag to prevent multiple submits for Order Form AJAX 2017-04-18 18:43:14 -05:00
Lance Edgar 589a747662 Auto-save time sheet day editor on Enter press if time field is focused
Hopefully this is a good idea, may have to revisit someday..?
2017-04-17 16:24:59 -05:00
Lance Edgar 56aff60efd Update changelog 2017-04-04 17:44:52 -05:00
Lance Edgar 754c086053 Fix signature for MasterView.get_index_url()
per new `mobile` kwarg
2017-04-04 17:43:33 -05:00
Lance Edgar 1a9a2b2d67 Update changelog 2017-04-04 16:05:34 -05:00
Lance Edgar e64cdb3f80 Tweak mobile home page to leverage config for main image 2017-03-30 23:32:18 -05:00
Lance Edgar a3d0966139 Allow config to define home page image URL 2017-03-30 22:19:44 -05:00
Lance Edgar 6156a80db0 Tweak field label styles for mobile 2017-03-30 20:15:09 -05:00
Lance Edgar 0ad29c5283 Add basic paging grid/index support for mobile
still lots to do for this yet..but readonly basics are here..
2017-03-30 20:11:17 -05:00
Lance Edgar e313e1bc8c Tweak logic for registering exception view, to avoid test breakage 2017-03-29 22:31:10 -05:00
Lance Edgar 17aab8f4f0 Update changelog 2017-03-29 12:46:42 -05:00
Lance Edgar 1adcb98f11 Various template standardization tweaks 2017-03-29 00:21:10 -05:00
Lance Edgar b1f98c1023 Add 'status' column to vendor cost table in product view
(available vs. discontinued)
2017-03-28 19:56:02 -05:00
Lance Edgar 5a2f20e489 Add basic table listing view, with rough estimate row counts 2017-03-28 00:54:46 -05:00
Lance Edgar 73c0d02b9a Only configure exception view when running in production
apparently that supresses normal traceback on console even..
2017-03-28 00:54:21 -05:00
Lance Edgar 7463d4e092 Add default view for unhandled exceptions
to give the user a bit of low-down as to what should happen next...
2017-03-28 00:00:35 -05:00
Lance Edgar 04e9752ee1 Detect "backwards" shift when time sheet is edited, alert user 2017-03-27 23:25:17 -05:00
Lance Edgar 27903b5984 Fix core view auto-logout inactive user logic, for tests 2017-03-27 22:44:51 -05:00
Lance Edgar 97aa17f64d Add logic to core View class, to force logout if user becomes inactive
Also, expose "active sticky" field for user views
2017-03-27 21:37:45 -05:00
Lance Edgar bef0a2d0b6 Tweak organization panel for product view template
for better customization
2017-03-26 18:42:20 -05:00
Lance Edgar 543bf5338a Update changelog 2017-03-25 15:34:33 -05:00
Lance Edgar 77a252399b Add unit item and pack size fields to product view 2017-03-25 15:28:38 -05:00
Lance Edgar 4a2f329613 Fix bugs when checking for 'chuck' in demo mode 2017-03-24 17:57:23 -05:00
Lance Edgar e37e17adf0 Fix route sequence for people autocomplete 2017-03-24 17:29:34 -05:00
Lance Edgar d373eb9ac1 Broad refactor to improve customization of purchase order form etc.
* add dropdown alternative for autocomplete renderer
* auto-enhance some common dropdowns
* refactor new purchase batch, order form view/templates
2017-03-24 17:22:12 -05:00
Lance Edgar e71204dcec Bump margin between grid and header table, i.e. buttons
hopefully this is a good amount..
2017-03-23 21:35:23 -05:00
Lance Edgar 3dfe3dfa28 Add CostFieldRenderer and tweak product view template
latter being for easier customization
2017-03-23 20:32:56 -05:00
Lance Edgar 95b2ce25c1 Update changelog 2017-03-22 19:26:10 -05:00
Lance Edgar 7975d177d6 Add file download support by default for report output 2017-03-22 14:35:40 -05:00
Lance Edgar 6adb99003d Add basic master view for Report Output data model 2017-03-22 14:27:59 -05:00
Lance Edgar e34bd947bc Allow config to override jQuery UI version 2017-03-21 21:23:35 -05:00
Lance Edgar 4c7f398c88 Add extra_main_fields() method to product view template 2017-03-21 19:35:32 -05:00
Lance Edgar 27c118eb10 Rollback our jQuery UI version again..to 1.11.4
apparently the new one introduces some changes which need to be
investigated further.  This change also makes it easier to override
the core jquery script tags, for experimenation...
2017-03-21 13:40:59 -05:00
Lance Edgar abb82c91f3 Add BatchMasterView.add_file_field() convenience method 2017-03-21 13:19:38 -05:00
Lance Edgar 0d830d595c Move notfound() method to core View class 2017-03-21 13:18:24 -05:00
Lance Edgar 5494266698 Bump default jQuery UI version to 1.12.1
previously was 1.11.4
2017-03-21 13:17:35 -05:00
Lance Edgar 43022c3205 Refactor the batch file field renderer somewhat
try to leverage the "common" file field renderer some more...
2017-03-21 13:16:48 -05:00
Lance Edgar 581a21bd9d Add basic "mobile index" master view, plus support for demo mode 2017-03-19 11:21:00 -05:00
Lance Edgar 9808bb3a91 Fix behavior of default email/phone field with empty value 2017-03-17 18:15:54 -05:00
Lance Edgar 15eae8b2c7 Various tweaks to the customer and person views/forms
These things still need plenty more help...
2017-03-17 15:52:26 -05:00
Lance Edgar e61b60e412 Add more variations of project name when creating via scaffold 2017-03-17 15:51:52 -05:00
Lance Edgar 68fea2f59a Add 'is_any' verb to integer grid filters 2017-03-16 13:54:40 -05:00
Lance Edgar 24765e8dac Update changelog 2017-03-14 12:36:12 -05:00
Lance Edgar 9a8fa43c6a Add trailing '?' for employee time sheet when hours are incomplete 2017-03-14 12:31:10 -05:00
Lance Edgar 693c2dce57 Tweak grid configuration for Employees view 2017-03-10 12:45:44 -06:00
Lance Edgar 7a63f11dae Update changelog 2017-03-03 15:58:22 -08:00
Lance Edgar b3599d8241 Add 'discontinued' flag to product view
Also, don't render product description as link if it's empty
2017-03-03 15:08:00 -08:00
Lance Edgar 2002031e41 Update changelog 2017-03-01 13:33:29 -08:00
Lance Edgar 7d7bdb11ae Add ingredients field to product view 2017-02-27 21:34:02 -08:00
Lance Edgar 89a4b5645c Add notes panel to product details view 2017-02-27 20:48:38 -08:00
Lance Edgar c3b15f76b5 Update changelog 2017-02-24 12:17:04 -06:00
Lance Edgar ec444d8f7d Remove 'forever sessions' permission from role/user views
This has been deprecated in favor of Role.session_timeout
2017-02-24 12:05:47 -06:00
Lance Edgar cf059baffa Add initial support for native product images
Definitely not perfect yet, but a start..
2017-02-23 13:21:19 -06:00
Lance Edgar 507f742edf Add some product flags (kosher vegan etc.) to view fieldset 2017-02-22 20:11:02 -06:00
Lance Edgar 555935c71e Expose notes field for purchasing batches
Plus various other tweaks for styles and coding conventions etc.
2017-02-21 14:27:16 -06:00
Lance Edgar 59799302bd Fix daylight savings bug when cloning schedule from previous week 2017-02-21 13:57:04 -06:00
Lance Edgar 75c73aad13 Expose/honor per-role session timeouts 2017-02-21 13:12:23 -06:00
Lance Edgar 7ef4ebcda8 Update changelog 2017-02-21 12:02:25 -06:00
Lance Edgar cb96272b46 Fix session reference bug in schedule view 2017-02-21 12:01:40 -06:00
Lance Edgar 7d18766aa1 Update changelog 2017-02-21 11:59:13 -06:00
Lance Edgar ac8c63219c Fix bug in DateFieldRenderer when no format specified 2017-02-21 11:58:21 -06:00
Lance Edgar 48713bb3cf Update changelog 2017-02-21 11:40:20 -06:00
Lance Edgar 6b11eb84ea Be less aggressive when validating schedule edit form POST
Somehow deletions were requested for shifts which didn't exist...not
sure how that happened but let's just ignore instead of raise error
2017-02-21 11:36:36 -06:00
Lance Edgar 080e3080af Tweak filter for CustomerOrderItem grid 2017-02-21 11:36:17 -06:00
Lance Edgar e0521ba8c5 Add initial/basic views for customer orders data 2017-02-19 14:22:35 -06:00
Lance Edgar 9b85a77695 Update changelog 2017-02-19 13:22:54 -06:00
Lance Edgar 3930ed9a16 Add beginnings of mobile receiving views
Very incomplete, not much is supported yet, but this is a start..
2017-02-19 13:17:28 -06:00
Lance Edgar 6ed752d477 Add generic "bulk delete" support to MasterView 2017-02-17 19:40:21 -06:00
Lance Edgar 9712868406 Update changelog 2017-02-17 14:51:33 -06:00
Lance Edgar 38aaebe08f Add ability to filter Send Messages by recipient name 2017-02-17 14:24:30 -06:00
Lance Edgar def2931ba6 Increase size of Roles select when editing a User 2017-02-17 14:24:13 -06:00
Lance Edgar 93f40ef36e Add ability to merge 2 user accounts 2017-02-17 12:49:15 -06:00
611 changed files with 89651 additions and 26297 deletions

3
.gitignore vendored
View file

@ -1,5 +1,8 @@
*~
*.pyc
.coverage .coverage
.tox/ .tox/
dist/
docs/_build/ docs/_build/
htmlcov/ htmlcov/
Tailbone.egg-info/ Tailbone.egg-info/

654
CHANGELOG.md Normal file
View file

@ -0,0 +1,654 @@
# Changelog
All notable changes to Tailbone will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## v0.22.3 (2024-11-19)
### Fix
- avoid error for trainwreck query when not a customer
## v0.22.2 (2024-11-18)
### Fix
- use local/custom enum for continuum operations
- add basic master view for Product Costs
- show continuum operation type when viewing version history
- always define `app` attr for ViewSupplement
- avoid deprecated import
## v0.22.1 (2024-11-02)
### Fix
- fix submit button for running problem report
- avoid deprecated grid method
## v0.22.0 (2024-10-22)
### Feat
- add support for new ordering batch from parsed file
### Fix
- avoid deprecated method to suggest username
## v0.21.11 (2024-10-03)
### Fix
- custom method for adding grid action
- become/stop root should redirect to previous url
## v0.21.10 (2024-09-15)
### Fix
- update project repo links, kallithea -> forgejo
- use better icon for submit button on login page
- wrap notes text for batch view
- expose datasync consumer batch size via configure page
## v0.21.9 (2024-08-28)
### Fix
- render custom attrs in form component tag
## v0.21.8 (2024-08-28)
### Fix
- ignore session kwarg for `MasterView.make_row_grid()`
## v0.21.7 (2024-08-28)
### Fix
- avoid error when form value cannot be obtained
## v0.21.6 (2024-08-28)
### Fix
- avoid error when grid value cannot be obtained
## v0.21.5 (2024-08-28)
### Fix
- set empty string for "-new-" file configure option
## v0.21.4 (2024-08-26)
### Fix
- handle differing email profile keys for appinfo/configure
## v0.21.3 (2024-08-26)
### Fix
- show non-standard config values for app info configure email
## v0.21.2 (2024-08-26)
### Fix
- refactor waterpark base template to use wutta feedback component
- fix input/output file upload feature for configure pages, per oruga
- tweak how grid data translates to Vue template context
- merge filters into main grid template
- add basic wutta view for users
- some fixes for wutta people view
- various fixes for waterpark theme
- avoid deprecated `component` form kwarg
## v0.21.1 (2024-08-22)
### Fix
- misc. bugfixes per recent changes
## v0.21.0 (2024-08-22)
### Feat
- move "most" filtering logic for grid class to wuttaweb
- inherit from wuttaweb templates for home, login pages
- inherit from wuttaweb for AppInfoView, appinfo/configure template
- add "has output file templates" config option for master view
### Fix
- change grid reset-view param name to match wuttaweb
- move "searchable columns" grid feature to wuttaweb
- use wuttaweb to get/render csrf token
- inherit from wuttaweb for appinfo/index template
- prefer wuttaweb config for "home redirect to login" feature
- fix master/index template rendering for waterpark theme
- fix spacing for navbar logo/title in waterpark theme
## v0.20.1 (2024-08-20)
### Fix
- fix default filter verbs logic for workorder status
## v0.20.0 (2024-08-20)
### Feat
- add new 'waterpark' theme, based on wuttaweb w/ vue2 + buefy
- refactor templates to simplify base/page/form structure
### Fix
- avoid deprecated reference to app db engine
## v0.19.3 (2024-08-19)
### Fix
- add pager stats to all grid vue data (fixes view history)
## v0.19.2 (2024-08-19)
### Fix
- sort on frontend for appinfo package listing grid
- prefer attr over key lookup when getting model values
- replace all occurrences of `component_studly` => `vue_component`
## v0.19.1 (2024-08-19)
### Fix
- fix broken user auth for web API app
## v0.19.0 (2024-08-18)
### Feat
- move multi-column grid sorting logic to wuttaweb
- move single-column grid sorting logic to wuttaweb
### Fix
- fix misc. errors in grid template per wuttaweb
- fix broken permission directives in web api startup
## v0.18.0 (2024-08-16)
### Feat
- move "basic" grid pagination logic to wuttaweb
- inherit from wutta base class for Grid
- inherit most logic from wuttaweb, for GridAction
### Fix
- avoid route error in user view, when using wutta people view
- fix some more wutta compat for base template
## v0.17.0 (2024-08-15)
### Feat
- use wuttaweb for `get_liburl()` logic
## v0.16.1 (2024-08-15)
### Fix
- improve wutta People view a bit
- update references to `get_class_hierarchy()`
- tweak template for `people/view_profile` per wutta compat
## v0.16.0 (2024-08-15)
### Feat
- add first wutta-based master, for PersonView
- refactor forms/grids/views/templates per wuttaweb compat
## v0.15.6 (2024-08-13)
### Fix
- avoid `before_render` subscriber hook for web API
- simplify verbiage for batch execution panel
## v0.15.5 (2024-08-09)
### Fix
- assign convenience attrs for all views (config, app, enum, model)
## v0.15.4 (2024-08-09)
### Fix
- avoid bug when checking current theme
## v0.15.3 (2024-08-08)
### Fix
- fix timepicker `parseTime()` when value is null
## v0.15.2 (2024-08-06)
### Fix
- use auth handler, avoid legacy calls for role/perm checks
## v0.15.1 (2024-08-05)
### Fix
- move magic `b` template context var to wuttaweb
## v0.15.0 (2024-08-05)
### Feat
- move more subscriber logic to wuttaweb
### Fix
- use wuttaweb logic for `util.get_form_data()`
## v0.14.5 (2024-08-03)
### Fix
- use auth handler instead of deprecated auth functions
- avoid duplicate `partial` param when grid reloads data
## v0.14.4 (2024-07-18)
### Fix
- fix more settings persistence bug(s) for datasync/configure
- fix modals for luigi tasks page, per oruga
## v0.14.3 (2024-07-17)
### Fix
- fix auto-collapse title for viewing trainwreck txn
- allow auto-collapse of header when viewing trainwreck txn
## v0.14.2 (2024-07-15)
### Fix
- add null menu handler, for use with API apps
## v0.14.1 (2024-07-14)
### Fix
- update usage of auth handler, per rattail changes
- fix model reference in menu handler
- fix bug when making "integration" menus
## v0.14.0 (2024-07-14)
### Feat
- move core menu logic to wuttaweb
## v0.13.2 (2024-07-13)
### Fix
- fix logic bug for datasync/config settings save
## v0.13.1 (2024-07-13)
### Fix
- fix settings persistence bug(s) for datasync/configure page
## v0.13.0 (2024-07-12)
### Feat
- begin integrating WuttaWeb as upstream dependency
### Fix
- cast enum as list to satisfy deform widget
## v0.12.1 (2024-07-11)
### Fix
- refactor `config.get_model()` => `app.model`
## v0.12.0 (2024-07-09)
### Feat
- drop python 3.6 support, use pyproject.toml (again)
## v0.11.10 (2024-07-05)
### Fix
- make the Members tab optional, for profile view
## v0.11.9 (2024-07-05)
### Fix
- do not show flash message when changing app theme
- improve collapse panels for butterball theme
- expand input for butterball theme
- add xref button to customer profile, for trainwreck txn view
- add optional Transactions tab for profile view
## v0.11.8 (2024-07-04)
### Fix
- fix grid action icons for datasync/configure, per oruga
- allow view supplements to add extra links for profile employee tab
- leverage import handler method to determine command/subcommand
- add tool to make user account from profile view
## v0.11.7 (2024-07-04)
### Fix
- add stacklevel to deprecation warnings
- require zope.sqlalchemy >= 1.5
- include edit profile email/phone dialogs only if user has perms
- allow view supplements to add to profile member context
- cast enum as list to satisfy deform widget
- expand POD image URL setting input
## v0.11.6 (2024-07-01)
### Fix
- set explicit referrer when changing dbkey
- remove references, dependency for `six` package
## v0.11.5 (2024-06-30)
### Fix
- allow comma in numeric filter input
- add custom url prefix if needed, for fanstatic
- use vue 3.4.31 and oruga 0.8.12 by default
## v0.11.4 (2024-06-30)
### Fix
- start/stop being root should submit POST instead of GET
- require vendor when making new ordering batch via api
- don't escape each address for email attempts grid
## v0.11.3 (2024-06-28)
### Fix
- add link to "resolved by" user for pending products
- handle error when merging 2 records fails
## v0.11.2 (2024-06-18)
### Fix
- hide certain custorder settings if not applicable
- use different logic for buefy/oruga for product lookup keydown
- product records should be touchable
- show flash error message if resolve pending product fails
## v0.11.1 (2024-06-14)
### Fix
- revert back to setup.py + setup.cfg
## v0.11.0 (2024-06-10)
### Feat
- switch from setup.cfg to pyproject.toml + hatchling
## v0.10.16 (2024-06-10)
### Feat
- standardize how app, package versions are determined
### Fix
- avoid deprecated config methods for app/node title
## v0.10.15 (2024-06-07)
### Fix
- do *not* Use `pkg_resources` to determine package versions
## v0.10.14 (2024-06-06)
### Fix
- use `pkg_resources` to determine package versions
## v0.10.13 (2024-06-06)
### Feat
- remove old/unused scaffold for use with `pcreate`
- add 'fanstatic' support for sake of libcache assets
## v0.10.12 (2024-06-04)
### Feat
- require pyramid 2.x; remove 1.x-style auth policies
- remove version cap for deform
- set explicit referrer when changing app theme
- add `<b-tooltip>` component shim
- include extra styles from `base_meta` template for butterball
- include butterball theme by default for new apps
### Fix
- fix product lookup component, per butterball
## v0.10.11 (2024-06-03)
### Feat
- fix vue3 refresh bugs for various views
- fix grid bug for tempmon appliance view, per oruga
- fix ordering worksheet generator, per butterball
- fix inventory worksheet generator, per butterball
## v0.10.10 (2024-06-03)
### Feat
- more butterball fixes for "view profile" template
### Fix
- fix focus for `<b-select>` shim component
## v0.10.9 (2024-06-03)
### Feat
- let master view control context menu items for page
- fix the "new custorder" page for butterball
### Fix
- fix panel style for PO vs. Invoice breakdown in receiving batch
## v0.10.8 (2024-06-02)
### Feat
- add styling for checked grid rows, per oruga/butterball
- fix product view template for oruga/butterball
- allow per-user custom styles for butterball
- use oruga 0.8.9 by default
## v0.10.7 (2024-06-01)
### Feat
- add setting to allow decimal quantities for receiving
- log error if registry has no rattail config
- add column filters for import/export main grid
- escape all unsafe html for grid data
- add speedbumps for delete, set preferred email/phone in profile view
- fix file upload widget for oruga
### Fix
- fix overflow when instance header title is too long (butterball)
## v0.10.6 (2024-05-29)
### Feat
- add way to flag organic products within lookup dialog
- expose db picker for butterball theme
- expose quickie lookup for butterball theme
- fix basic problems with people profile view, per butterball
## v0.10.5 (2024-05-29)
### Feat
- add `<tailbone-timepicker>` component for oruga
## v0.10.4 (2024-05-12)
### Fix
- fix styles for grid actions, per butterball
## v0.10.3 (2024-05-10)
### Fix
- fix bug with grid date filters
## v0.10.2 (2024-05-08)
### Feat
- remove version restriction for pyramid_beaker dependency
- rename some attrs etc. for buefy components used with oruga
- fix "tools" helper for receiving batch view, per oruga
- more data type fixes for ``<tailbone-datepicker>``
- fix "view receiving row" page, per oruga
- tweak styles for grid action links, per butterball
### Fix
- fix employees grid when viewing department (per oruga)
- fix login "enter" key behavior, per oruga
- fix button text for autocomplete
## v0.10.1 (2024-04-28)
### Feat
- sort list of available themes
- update various icon names for oruga compatibility
- show "View This" button when cloning a record
- stop including 'falafel' as available theme
### Fix
- fix vertical alignment in main menu bar, for butterball
- fix upgrade execution logic/UI per oruga
## v0.10.0 (2024-04-28)
This version bump is to reflect adding support for Vue 3 + Oruga via
the 'butterball' theme. There is likely more work to be done for that
yet, but it mostly works at this point.
### Feat
- misc. template and view logic tweaks (applicable to all themes) for
better patterns, consistency etc.
- add initial support for Vue 3 + Oruga, via "butterball" theme
## Older Releases
Please see `docs/OLDCHANGES.rst` for older release notes.

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
GNU AFFERO GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
Version 3, 19 November 2007 Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
@ -7,15 +7,17 @@
Preamble Preamble
The GNU Affero General Public License is a free, copyleft license for The GNU General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure software and other kinds of works.
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast, to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free share and change all versions of a program--to make sure it remains free
software for all its users. software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you price. Our General Public Licenses are designed to make sure that you
@ -24,34 +26,44 @@ them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things. free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights To protect your rights, we need to prevent others from denying you
with two steps: (1) assert copyright on the software, and (2) offer these rights or asking you to surrender the rights. Therefore, you have
you this License which gives you legal permission to copy, distribute certain responsibilities if you distribute copies of the software, or if
and/or modify the software. you modify it: responsibilities to respect the freedom of others.
A secondary benefit of defending all users' freedom is that For example, if you distribute copies of such a program, whether
improvements made in alternate versions of the program, if they gratis or for a fee, you must pass on to the recipients the same
receive widespread use, become available for other developers to freedoms that you received. You must make sure that they, too, receive
incorporate. Many developers of free software are heartened and or can get the source code. And you must show them these terms so they
encouraged by the resulting cooperation. However, in the case of know their rights.
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to Developers that use the GNU GPL protect your rights with two steps:
ensure that, in such cases, the modified source code becomes available (1) assert copyright on the software, and (2) offer you this License
to the community. It requires the operator of a network server to giving you legal permission to copy, distribute and/or modify it.
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and For the developers' and authors' protection, the GPL clearly explains
published by Affero, was designed to accomplish similar goals. This is that there is no warranty for this free software. For both users' and
a different license, not a version of the Affero GPL, but Affero has authors' sake, the GPL requires that modified versions be marked as
released a new version of the Affero GPL which permits relicensing under changed, so that their problems will not be attributed erroneously to
this license. authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and The precise terms and conditions for copying, distribution and
modification follow. modification follow.
@ -60,7 +72,7 @@ modification follow.
0. Definitions. 0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License. "This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks. works, such as semiconductor masks.
@ -537,45 +549,35 @@ to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program. License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License. 13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work, License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version but the special requirements of the GNU Affero General Public License,
3 of the GNU General Public License. section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License. 14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions the GNU General Public License from time to time. Such new versions will
will be similar in spirit to the present version, but may differ in detail to be similar in spirit to the present version, but may differ in detail to
address new problems or concerns. address new problems or concerns.
Each version is given a distinguishing version number. If the Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published GNU General Public License, you may choose any version ever published
by the Free Software Foundation. by the Free Software Foundation.
If the Program specifies that a proxy can decide which future If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you public statement of acceptance of a version permanently authorizes you
to choose that version for the Program. to choose that version for the Program.
@ -633,29 +635,40 @@ the "copyright" line and a pointer to where the full notice is found.
Copyright (C) <year> <name of author> Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer If the program does terminal interaction, make it output a short
network, you should also make sure that it provides a way for users to notice like this when it starts in an interactive mode:
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive <program> Copyright (C) <year> <name of author>
of the code. There are many ways you could offer source, and different This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
solutions will be better for different programs; see section 13 for the This is free software, and you are welcome to redistribute it
specific requirements. under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school, You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary. if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>. <http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View file

@ -3,6 +3,7 @@ include *.txt
include *.rst include *.rst
include *.py include *.py
include tailbone/static/robots.txt
recursive-include tailbone/static *.js recursive-include tailbone/static *.js
recursive-include tailbone/static *.css recursive-include tailbone/static *.css
recursive-include tailbone/static *.png recursive-include tailbone/static *.png
@ -10,5 +11,8 @@ recursive-include tailbone/static *.jpg
recursive-include tailbone/static *.gif recursive-include tailbone/static *.gif
recursive-include tailbone/static *.ico recursive-include tailbone/static *.ico
recursive-include tailbone/static/files *
recursive-include tailbone/templates *.mako recursive-include tailbone/templates *.mako
recursive-include tailbone/templates *.pt
recursive-include tailbone/reports *.mako recursive-include tailbone/reports *.mako

View file

@ -1,10 +1,8 @@
Tailbone # Tailbone
========
Tailbone is an extensible web application based on Rattail. It provides a Tailbone is an extensible web application based on Rattail. It provides a
"back-office network environment" (BONE) for use in managing retail data. "back-office network environment" (BONE) for use in managing retail data.
Please see Rattail's `home page`_ for more information. Please see Rattail's [home page](http://rattailproject.org/) for more
information.
.. _home page: http://rattailproject.org/

7539
docs/OLDCHANGES.rst Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,15 @@
``tailbone.api.batch.core``
===========================
.. automodule:: tailbone.api.batch.core
.. autoclass:: APIBatchMixin
.. autoclass:: APIBatchView
.. autoclass:: APIBatchRowView
.. autoattribute:: editable
.. autoattribute:: supports_quick_entry

View file

@ -0,0 +1,41 @@
``tailbone.api.batch.ordering``
===============================
.. automodule:: tailbone.api.batch.ordering
.. autoclass:: OrderingBatchViews
.. autoattribute:: collection_url_prefix
.. autoattribute:: object_url_prefix
.. autoattribute:: model_class
.. autoattribute:: route_prefix
.. autoattribute:: permission_prefix
.. autoattribute:: default_handler_spec
.. automethod:: base_query
.. automethod:: create_object
.. autoclass:: OrderingBatchRowViews
.. autoattribute:: collection_url_prefix
.. autoattribute:: object_url_prefix
.. autoattribute:: model_class
.. autoattribute:: route_prefix
.. autoattribute:: permission_prefix
.. autoattribute:: default_handler_spec
.. autoattribute:: supports_quick_entry
.. automethod:: update_object

6
docs/api/db.rst Normal file
View file

@ -0,0 +1,6 @@
``tailbone.db``
===============
.. automodule:: tailbone.db
:members:

6
docs/api/diffs.rst Normal file
View file

@ -0,0 +1,6 @@
``tailbone.diffs``
==================
.. automodule:: tailbone.diffs
:members:

9
docs/api/forms.rst Normal file
View file

@ -0,0 +1,9 @@
``tailbone.forms``
==================
.. automodule:: tailbone.forms
:members:
.. autoclass:: tailbone.forms.Form
:members:

View file

@ -0,0 +1,6 @@
``tailbone.forms.widgets``
==========================
.. automodule:: tailbone.forms.widgets
:members:

6
docs/api/grids.core.rst Normal file
View file

@ -0,0 +1,6 @@
``tailbone.grids.core``
=======================
.. automodule:: tailbone.grids.core
:members:

6
docs/api/grids.rst Normal file
View file

@ -0,0 +1,6 @@
``tailbone.grids``
==================
.. automodule:: tailbone.grids
:members:

View file

@ -1,10 +0,0 @@
.. -*- coding: utf-8 -*-
``tailbone.newgrids``
=====================
.. automodule:: tailbone.newgrids
:members:
.. automodule:: tailbone.newgrids.alchemy
:members:

6
docs/api/progress.rst Normal file
View file

@ -0,0 +1,6 @@
``tailbone.progress``
=====================
.. automodule:: tailbone.progress
:members:

View file

@ -3,5 +3,4 @@
======================== ========================
.. automodule:: tailbone.subscribers .. automodule:: tailbone.subscribers
:members:
.. autofunction:: add_rattail_config_attribute_to_request

6
docs/api/util.rst Normal file
View file

@ -0,0 +1,6 @@
``tailbone.util``
=================
.. automodule:: tailbone.util
:members:

View file

@ -1,29 +1,5 @@
.. -*- coding: utf-8 -*-
``tailbone.views.batch`` ``tailbone.views.batch``
======================== ========================
.. automodule:: tailbone.views.batch .. automodule:: tailbone.views.batch
.. autoclass:: BatchGrid
:members:
.. autoclass:: FileBatchGrid
:members:
.. autoclass:: BatchCrud
:members:
.. autoclass:: FileBatchCrud
:members:
.. autoclass:: BatchRowGrid
:members:
.. autoclass:: ProductBatchRowGrid
:members:
.. autoclass:: BatchRowCrud
:members:
.. autofunction:: defaults

View file

@ -0,0 +1,10 @@
``tailbone.views.batch.vendorcatalog``
======================================
.. automodule:: tailbone.views.batch.vendorcatalog
.. autoclass:: VendorCatalogsView
:members:
.. autofunction:: includeme

6
docs/api/views/core.rst Normal file
View file

@ -0,0 +1,6 @@
``tailbone.views.core``
=======================
.. automodule:: tailbone.views.core
:members:

View file

@ -1,4 +1,3 @@
.. -*- coding: utf-8 -*-
``tailbone.views.master`` ``tailbone.views.master``
========================= =========================
@ -67,12 +66,61 @@ override when defining your subclass.
.. attribute:: MasterView.grid_factory .. attribute:: MasterView.grid_factory
Factory callable to be used when creating new grid instances; defaults to Factory callable to be used when creating new grid instances; defaults to
:class:`tailbone.newgrids.alchemy.AlchemyGrid`. :class:`tailbone.grids.Grid`.
.. Methods to Override .. attribute:: MasterView.results_downloadable_csv
.. -------------------
.. Flag indicating whether the view should allow CSV download of grid data,
.. The following is a list of methods which you can override when defining your i.e. primary search results.
.. subclass.
.. .. attribute:: MasterView.help_url
.. .. automethod:: MasterView.get_settings
If set, this defines the "default" help URL for all views provided by the
master. Default value for this is simply ``None`` which would mean the
Help button is not shown at all. Note that the master may choose to
override this for certain views, if so that should be done within
:meth:`get_help_url()`.
.. attribute:: MasterView.version_diff_factory
Optional factory to use for version diff objects. By default
this is *not set* but a subclass is free to set it. See also
:meth:`get_version_diff_factory()`.
Methods to Override
-------------------
The following is a list of methods which you can override when defining your
subclass.
.. automethod:: MasterView.editable_instance
.. .. automethod:: MasterView.get_settings
.. automethod:: MasterView.get_csv_fields
.. automethod:: MasterView.get_csv_row
.. automethod:: MasterView.get_help_url
.. automethod:: MasterView.get_model_key
.. automethod:: MasterView.get_version_diff_enums
.. automethod:: MasterView.get_version_diff_factory
.. automethod:: MasterView.make_version_diff
.. automethod:: MasterView.title_for_version
Support Methods
---------------
The following is a list of methods you should (probably) not need to
override, but may find useful:
.. automethod:: MasterView.default_edit_url
.. automethod:: MasterView.get_action_route_kwargs

View file

@ -0,0 +1,6 @@
``tailbone.views.members``
==========================
.. automodule:: tailbone.views.members
:members:

View file

@ -0,0 +1,9 @@
``tailbone.views.purchasing.batch``
===================================
.. automodule:: tailbone.views.purchasing.batch
.. autoclass:: PurchasingBatchView
.. automethod:: save_edit_row_form

View file

@ -0,0 +1,15 @@
``tailbone.views.purchasing.ordering``
======================================
.. automodule:: tailbone.views.purchasing.ordering
.. autoclass:: OrderingBatchView
.. autoattribute:: model_class
.. autoattribute:: default_handler_spec
.. automethod:: configure_row_form
.. automethod:: worksheet_update

View file

@ -1,10 +0,0 @@
``tailbone.views.vendors.catalogs``
===================================
.. automodule:: tailbone.views.vendors.catalogs
.. autoclass:: VendorCatalogsView
:members:
.. autofunction:: includeme

8
docs/changelog.rst Normal file
View file

@ -0,0 +1,8 @@
Changelog Archive
=================
.. toctree::
:maxdepth: 1
OLDCHANGES

65
docs/concepts/batches.rst Normal file
View file

@ -0,0 +1,65 @@
Data Batches
============
.. contents:: :local:
Data "batches" are one of the most powerful features of Rattail / Tailbone.
However each "batch type" is different, and they usually require custom
development. In all cases they require a Rattail-based app database, for
storage.
General Overview
----------------
You can think of data batches as a sort of "temporary spreadsheet" feature.
When a batch is created, it is usually populated with rows, from some data
source. The user(s) may then manipulate the batch data as needed, with the
final goal being to "execute" the batch. What execution specifically means
will depend on context, e.g. type of batch, but generally it will "commit" the
"pending changes" which are represented by the batch.
Note that when a batch is executed, it becomes read-only ("frozen in time") and
at that point may be considered part of an audit trail of sorts. The utility
of this may vary depending on the nature of the batch data.
Beyond that it's difficult to describe batches very well at this level,
precisely because they're all different.
..
This graphic tries to show how batches are created and executed over time.
Note that each batch type is free to target a different system(s) upon
execution.
TODO: need graphic
Batch Tables
------------
In most cases the table(s) underlying a particular batch type, have a "static"
schema and must be defined as ORM classes, e.g. within the ``poser.db.model``
package.
In some rare cases the batch data (row) table may be dynamic; however the batch
header table must still be defined.
Batch Handlers
--------------
Once the batch table(s) are present, the next puzzle piece is the batch
handler. Again there is generally (at least) one handler defined for each
batch type.
The batch "handler" is considered part of the data layer and provides logic for
populating the batch, executing it etc.
Batch Views
-----------
This discussion would not be complete without mentioning the web views for the
batch. Again each batch type will require a custom view(s) although these
"usually" are simple wrappers as most logic is provided by the base view.

115
docs/concepts/config.rst Normal file
View file

@ -0,0 +1,115 @@
Configuration
=============
.. contents:: :local:
Configuration for an app can come from two sources: configuration file(s), and
the Settings table in the database.
Config File Inheritance
-----------------------
An important thing to understand regarding Rattail config files, is that one
file may "include" another file(s), which in turn may "include" others etc.
Invocation of the app will often require only a single config file to be
specified, since that file may include others as needed.
For example ``web.conf`` will typically include ``rattail.conf`` but the web
app need only be invoked with ``web.conf`` - config from both files will inform
the app's behavior.
Typical Config Files
--------------------
A typical Poser (Rattail-based) app will have at the very least, one file named
``rattail.conf`` - this is considered the most fundamental config file. It
will usually define database connections, logging config, and any other "core"
things which would be required for any invocation of the app, regardless of the
environment (e.g. console vs. web).
Note that even ``rattail.conf`` is free to include other files. This may be
useful for instance, if you have a single site-wide config file which is shared
among all Rattail apps.
There is no *strict* requirement for having a ``rattail.conf`` file, but these
docs will assume its presence. Here are some other typical files, which the
docs also may reference occasionally:
**web.conf** - This is the "core" config file for the web app, although it
still includes the ``rattail.conf`` file. In production (running on Apache
etc.) it is specified within the WSGI module which is responsible for
instantiating the web app. When running the development server, it is
specified via command line.
**quiet.conf** - This is a slight wrapper around ``rattail.conf`` for the sake
of a "quieter" console, when running app commands via console. It may be used
in place of ``rattail.conf`` - i.e. you would specify ``-c quiet.conf`` when
running the command. The only function of this wrapper is to set the level to
INFO for the console logging handler. In practice this hides DEBUG logging
messages which are shown by default when using ``rattail.conf`` as the app
config file.
**cron.conf** - Another wrapper around ``rattail.conf`` which suppresses
logging even further. The idea is that this config file would be used by cron
jobs; that way the only actual output is warnings and errors, hence cron would
not send email unless something actually went wrong. It may be used in place
of ``rattail.conf`` - i.e. you would specify ``-c cron.conf`` when running the
command. The only function of this wrapper is to set the level to WARNING for
the console logging handler.
**ignore-changes.conf** - This file is only relevant if your ``rattail.conf``
says to "record changes" when write activity occurs in the database(s). Note
that this file does *not* include ``rattail.conf`` because it is meant to be
supplemental only. For instance on the command line, you would need to specify
two config files, first ``rattail.conf`` or a suitable alternative, but then
``ignore-changes.conf`` also. If specified, this file will cause changes to be
ignored, i.e. **not recorded** when write activity occurs.
**without-versioning.conf** - This file is only relevant if your
``rattail.conf`` says to enable "data versioning" when write activity occurs in
the database(s). Note that this file does *not* include ``rattail.conf``
because it is meant to be supplemental only. For instance on the command line,
you would need to specify two config files, first ``rattail.conf`` or a
suitable alternative, but then ``without-versioning.conf`` also. If specified,
this file will disable the data versioning system entirely. Note that if
versioning is undesirable for a given app run, this is the only way to
effectively disable it; once loaded that feature cannot be disabled.
Settings from Database
----------------------
The other (often more convenient) source of app configuration is the Settings
table within the app database. Whether or not this table is a valid source for
app configuration, ultimately depends on what the config file(s) has to say
about it.
Assuming the config file(s) defines a database connection and declares it a
valid source for config values, then the Settings table may contribute to the
running app config. The nice thing about this is that these settings are
checked in real-time. So whereas changing a config file will require an app
restart, any edits to the settings table should take effect immediately.
Usually the settings table will *override* values found in the config file.
This behavior also is configurable to some extent, and in some cases a config
value may *only* come from a config file and never the settings table.
An example may help here. If the config file contained the following value:
.. code-block:: ini
[poser]
foo = bar
Then you could create a new Setting in the database with the following fields:
* **name** = poser.foo
* **value** = baz
Assuming typical setup, i.e. where settings table may override config file, the
app would consider 'baz' to be the config value. So basically the setting name
must correspond to a combination of the config file "section" name, then a dot,
then the "option" name.

View file

@ -0,0 +1,7 @@
Console Commands
================
.. contents:: :local:
TODO

45
docs/concepts/schema.rst Normal file
View file

@ -0,0 +1,45 @@
Database Schema
===============
.. contents:: :local:
Rattail provides a "core" schema which is assumed to be the foundation of any
Poser app database.
Core Tables
-----------
All tables which are considered part of the Rattail "core" schema, are defined
as ORM classes within the ``rattail.db.model`` package.
.. note::
The Rattail project has its roots in retail grocery-type stores, and its
schema reflects that to a large degree. In practice however the software
may be used to support a wide variety of apps. The next section describes
that a bit more.
Customizing the Schema
----------------------
Almost certainly a custom app will need some of the core tables, but just as
certainly, it will *not* need others. And to make things even more
interesting, it may need some tables but also need to "supplement" them
somehow, to track additional data for each record etc.
Any table in the core schema which is *not* needed, may simply be ignored,
i.e. hidden from the app UI etc.
Any table which is "missing" from core schema, from the custom app's
perspective, should be added as a custom table.
Also, any table which is "present but missing columns" from the app's
perspective, will require a custom table. In this case each record in the
custom table will "tie back to" the core table record. The custom record will
then supply any additional data for the core record.
Defining custom tables, and associated tasks, are documented in
:doc:`../schemachange`.

View file

@ -1,36 +1,21 @@
# -*- coding: utf-8 -*- # Configuration file for the Sphinx documentation builder.
# #
# Tailbone documentation build configuration file, created by # For the full list of built-in configuration values, see the documentation:
# sphinx-quickstart on Sat Feb 15 23:15:27 2014. # https://www.sphinx-doc.org/en/master/usage/configuration.html
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys # -- Project information -----------------------------------------------------
import os # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
execfile(os.path.join(os.pardir, 'tailbone', '_version.py')) from importlib.metadata import version as get_version
project = 'Tailbone'
copyright = '2010 - 2024, Lance Edgar'
author = 'Lance Edgar'
release = get_version('Tailbone')
# If extensions (or modules to document with autodoc) are in another directory, # -- General configuration ---------------------------------------------------
# add these directories to sys.path here. If the directory is relative to the # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [ extensions = [
'sphinx.ext.autodoc', 'sphinx.ext.autodoc',
'sphinx.ext.todo', 'sphinx.ext.todo',
@ -38,234 +23,30 @@ extensions = [
'sphinx.ext.viewcode', 'sphinx.ext.viewcode',
] ]
templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
intersphinx_mapping = { intersphinx_mapping = {
# TODO: Add this back, when the FA site is back online... 'rattail': ('https://rattailproject.org/docs/rattail/', None),
#'formalchemy': ('http://docs.formalchemy.org/formalchemy/', None), 'webhelpers2': ('https://webhelpers2.readthedocs.io/en/latest/', None),
'wuttaweb': ('https://rattailproject.org/docs/wuttaweb/', None),
'wuttjamaican': ('https://rattailproject.org/docs/wuttjamaican/', None),
} }
# Add any paths that contain templates here, relative to this directory. # allow todo entries to show up
templates_path = ['_templates'] todo_include_todos = True
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Tailbone'
copyright = u'2015, Lance Edgar'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.3'
# The full version, including alpha/beta/rc tags.
release = __version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# -- Options for HTML output ---------------------------------------------- # -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
# The theme to use for HTML and HTML Help pages. See the documentation for html_theme = 'furo'
# a list of builtin themes. html_static_path = ['_static']
html_theme = 'classic'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top # The name of an image file (relative to this directory) to place at the top
# of the sidebar. # of the sidebar.
#html_logo = None #html_logo = None
#html_logo = 'images/rattail_avatar.png'
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder. # Output file base name for HTML help builder.
htmlhelp_basename = 'Tailbonedoc' #htmlhelp_basename = 'Tailbonedoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'Tailbone.tex', u'Tailbone Documentation',
u'Lance Edgar', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'tailbone', u'Tailbone Documentation',
[u'Lance Edgar'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Tailbone', u'Tailbone Documentation',
u'Lance Edgar', 'Tailbone', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False

78
docs/devenv.rst Normal file
View file

@ -0,0 +1,78 @@
Development Environment
=======================
.. contents:: :local:
Base System
-----------
Development for Tailbone in particular is assumed to occur on a Linux machine.
This is because it's assumed that the web app would run on Linux. It should be
possible (presumably) to do either on Windows or Mac but that is not officially
supported.
Furthermore it is assumed the Linux flavor in use is either Debian or Ubuntu,
or a similar alternative. Presumably any Linux would work although some
details may differ from what's shown here.
Prerequisites
-------------
Python
^^^^^^
The only supported Python is 2.7. Of course that should already be present on
Linux.
It usually is required at some point to compile C code for certain Python
extension modules. In practice this means you probably want the Python header
files as well:
.. code-block:: sh
sudo apt-get install python-dev
pip
^^^
The only supported Python package manager is ``pip``. This can be installed a
few ways, one of which is:
.. code-block:: sh
sudo apt-get install python-pip
virtualenvwrapper
^^^^^^^^^^^^^^^^^
While not technically required, it is recommended to use ``virtualenvwrapper``
as well. There is more than one way to set this up, e.g.:
.. code-block:: sh
sudo apt-get install python-virtualenvwrapper
The main variable as concerns these docs, is where your virtual environment(s)
will live. If you install virtualenvwrapper via the above command, then most
likely your ``$WORKON_HOME`` environment variable will be set to
``~/.virtualenvs`` - however these docs will assume ``/srv/envs`` instead.
Please adjust any commands as needed.
PostgreSQL
^^^^^^^^^^
The other primary requirement is PostgreSQL. Technically that may be installed
on a separate machine, which allows connection from the development machine.
But of course it will usually just be installed on the dev machine:
.. code-block:: sh
sudo apt-get install postgresql
Regardless of where your PG server lives, you will probably need some extras in
order to compile extensions for the ``psycopg2`` package:
.. code-block:: sh
sudo apt-get install libpq-dev

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View file

@ -2,15 +2,35 @@
Tailbone Tailbone
======== ========
Welcome to Tailbone, part of the Rattail project. Welcome to Tailbone, part of the Rattail project. While the core Rattail
package provides the data layer, the Tailbone package provides the (default,
back-end) web application layer.
The documentation you are currently reading is for the Tailbone web application Some additional information is available on the `website`_. Certainly not
package. Some additional information is available on the `website`_. Clearly everything is documented yet, but here you can see what has received some
not everything is documented yet. Below you can see what has received some
attention thus far. attention thus far.
.. _website: https://rattailproject.org/ .. _website: https://rattailproject.org/
Quick Start for Custom Apps:
.. toctree::
:maxdepth: 1
structure
devenv
newproject
schemachange
Concept Guide:
.. toctree::
concepts/config
concepts/console
concepts/schema
concepts/batches
Narrative Documentation: Narrative Documentation:
.. toctree:: .. toctree::
@ -22,11 +42,32 @@ Package API:
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
api/newgrids api/api/batch/core
api/api/batch/ordering
api/db
api/diffs
api/forms
api/forms.widgets
api/grids
api/grids.core
api/progress
api/subscribers api/subscribers
api/util
api/views/batch api/views/batch
api/views/batch.vendorcatalog
api/views/core
api/views/master api/views/master
api/views/vendors.catalogs api/views/members
api/views/purchasing.batch
api/views/purchasing.ordering
Changelog:
.. toctree::
:maxdepth: 1
changelog
Documentation To-Do Documentation To-Do

154
docs/newproject.rst Normal file
View file

@ -0,0 +1,154 @@
Creating a New Project
======================
.. contents:: :local:
.. highlight:: bash
This describes the process of creating a new app project based on
Rattail/Tailbone. It assumes you are working from a supported :doc:`devenv`.
Per convention, this doc uses "Poser" (and ``poser``) to represent the custom
app. Please adjust commands etc. accordingly. See also :doc:`structure`.
Create the Virtual Environment
------------------------------
First step is simple enough::
mkvirtualenv poser
Then with your new environment activated, install the Tailbone package::
pip install Tailbone
Create the Project
------------------
Now with your environment still activated, ``cd`` to wherever you like
(e.g. ``~/src``) and create a new project skeleton like so::
mkdir -p ~/src
cd ~/src
pcreate -s rattail poser
This will have created a new project at ``~/src/poser`` which you can then edit
as you wish. At some point you will need to "install" this project to the
environment like so (again with environment active)::
cd ~/src/poser
pip install -e .
Setup the App Environment
-------------------------
Any project based on Rattail will effectively be its own "app" (usually), but
Rattail itself provides some app functionality as well. However all such apps
require config files, usually. If running a web app then you may also need to
have configured a folder for session storage, etc. To hopefully simplify all
this, there are a few commands you should now run, with your virtual
environment still active::
rattail make-appdir
cdvirtualenv app
rattail make-config -T rattail
rattail make-config -T quiet
rattail make-config -T web
This will have created a new 'app' folder in your environment (e.g. at
``/srv/envs/poser/app``) and then created ``rattail.conf`` and ``web.conf``
files within that app dir. Note that there will be other folders inside the
app dir as well; these are referenced by the config files.
But you're not done yet... You should likely edit the config files, at the
very least edit ``rattail.conf`` and change the ``default.url`` value (under
``[rattail.db]`` section) which defines the Rattail database connection.
Create the Database
-------------------
If applicable, it's time for that. First you must literally create the user
and database on your PostgreSQL server, e.g.::
sudo -u postgres createuser --no-createdb --no-createrole --no-superuser poser
sudo -u postgres psql -c "alter user poser password 'mypassword'"
sudo -u postgres createdb --owner poser poser
Then you can install the schema; with your virtual environment activated::
cdvirtualenv
alembic -c app/rattail.conf upgrade heads
At this point your 'poser' database should have some empty tables. To confirm,
on your PG server do::
sudo -u postgres psql -c '\d' poser
Create Admin User
-----------------
If your intention is to have a web app, or at least to test one, you'll
probably want to create the initial admin user. With your env active::
cdvirtualenv
rattail -c app/quiet.conf make-user --admin myusername
This should prompt you for a password, then create a single user and assign it
to the Administrator role.
Install Sample Data
-------------------
If desired, you can install a bit of sample data to your fresh Rattail
database. With your env active do::
cdvirtualenv
rattail -c app/quiet.conf -P import-sample
Run Dev Web Server
------------------
With all the above in place, you may now run the web server in dev mode::
cdvirtualenv
pserve --reload app/web.conf
And finally..you may browse your new project dev site at http://localhost:9080/
(unless you changed the port etc.)
Schema Migrations
-----------------
Often a new project will require custom schema additions to track/manage data
unique to the project. Rattail uses `Alembic`_ for handling schema migrations.
General usage of that is documented elsewhere, but a little should be said here
regarding new projects.
.. _Alembic: https://pypi.python.org/pypi/alembic
The new project template includes most of an Alembic "repo" for schema
migrations. However there is one step required to really bootstrap it, i.e. to
the point where normal Alembic usage will work: you must create the initial
version script. Before you do this, you should be reasonably happy with any
ORM classes you've defined, as the initial version script will be used to
create that schema. Once you're ready for the script, this command should do
it::
cdvirtualenv
bin/alembic -c app/rattail.conf revision --autogenerate --version-path ~/src/poser/poser/db/alembic/versions/ -m 'initial Poser tables'
You should of course look over and edit the generated script as needed. One
change in particular you should make is to add a branch label, e.g.:
.. code-block:: python
branch_labels = ('poser',)

63
docs/schemachange.rst Normal file
View file

@ -0,0 +1,63 @@
Migrating the Schema
====================
.. contents:: :local:
As development progresses for your custom app, you may need to migrate the
database schema from time to time.
See also this general discussion of the :doc:`concepts/schema`.
.. note::
The only "safe" migrations are those which add or modify (or remove)
"custom" tables, i.e. those *not* provided by the ``rattail.db.model``
package. This doc assumes you are aware of this and are only attempting a
safe migration.
Modify ORM Classes
------------------
First step is to modify the ORM classes defined by your app, so they reflect
the "desired" schema. Typically this will mean editing files under the
``poser.db.model`` package within your source. In particular when adding new
tables, you must be sure to include them within ``poser/db/model/__init__.py``.
As noted above, only those classes *not* provided by ``rattail.db.model``
should be modified here, to be safe. If you wish to "extend" an existing
table, you must create a secondary table which ties back to the first via
one-to-one foreign key relationship.
Create Migration Script
-----------------------
Next you will create the Alembic script which is responsible for performing the
schema migration against a database. This is typically done like so:
.. code-block:: sh
workon poser
cdvirtualenv
bin/alembic -c app/rattail.conf revision --autogenerate --head poser@head -m "describe migration here"
This will create a new file under
e.g. ``~/src/poser/poser/db/alembic/versions/``. You should edit this file as
needed to ensure it performs all steps required for the migration. Technically
it should support downgrade as well as upgrade, although in practice that isn't
always required.
Upgrade Database Schema
-----------------------
Once you're happy with the new script, you can apply it against your dev
database with something like:
.. code-block:: sh
workon poser
cdvirtualenv
bin/alembic -c app/rattail.conf upgrade heads

130
docs/structure.rst Normal file
View file

@ -0,0 +1,130 @@
App Organization & Structure
============================
.. contents:: :local:
Tailbone doesn't try to be an "app" proper. But it does try to provide just
about everything you'd need to make one. These docs assume you are making a
custom app, and will refer to the app as "Poser" to be consistent. In practice
you would give your app a unique name which is meaningful to you. Please
mentally replace "Poser" with your app name as you read.
.. note::
Technically it *is possible* to use Tailbone directly as the app. You may
do so for basic testing of the concepts, but you'd be stuck with Tailbone
logic, with far fewer customization options. All docs will assume a custom
"Poser" app which wraps and (as necessary) overrides Tailbone and Rattail.
Architecture
------------
In terms of how the Poser app hangs together, here is a conceptual diagram.
Note that all systems on the right-hand side are *external* to Poser, i.e. they
are not "plugins" although Poser may use plugin-like logic for the sake of
integrating with these systems.
.. image:: images/poser-architecture.png
Data Layer vs. Web Layer
^^^^^^^^^^^^^^^^^^^^^^^^
While the above graphic doesn't do a great job highlighting the difference, it
will (presumably) help to understand the difference in purpose and function of
Tailbone vs. Rattail packages.
**Rattail** is the data layer, and is responsible for database connectivity,
table schema information, and some business rules logic (among other things).
**Tailbone** is the web app layer, and is responsible for presentation and
management of data objects which are made available by Rattail (and others).
**Poser** is a custom layer which can make use of both data and web app layers,
supplementing each as necessary. In practice the lines may get blurry within
Poser.
The reason for this distinction between layers, is to allow creation of custom
apps which use only the data layer but not the web app layer. This can be
useful for console-based apps; a traditional GUI app would also be possible
although none is yet planned.
File Layout
-----------
Below is an example file layout for a Poser app project. This tries to be
"complete" and show most kinds of files a typical project may need. In
practice you can usually ignore anything which doesn't apply to your app,
i.e. relatively few of the files shown here are actually required. Of course
some apps may need many more files than this to achieve their goals.
Note that all files in the root ``poser`` package namespace would correspond to
the "data layer" mentioned above, whereas everything under ``poser.web`` would
of course supply the web app layer.
.. code-block:: none
~/src/poser/
├── CHANGELOG.md
├── docs/
├── fabfile.py
├── MANIFEST.in
├── poser/
│   ├── __init__.py
│   ├── batch/
│   │   ├── __init__.py
│   │   └── foobatch.py
│   ├── commands.py
│   ├── config.py
│   ├── datasync/
│   ├── db/
│   │   ├── __init__.py
│   │   ├── alembic/
│   │   └── model/
│   │   ├── __init__.py
│   │   ├── batch/
│   │   │   ├── __init__.py
│   │   │   └── foobatch.py
│   │   └── customers.py
│   ├── emails.py
│   ├── enum.py
│   ├── importing/
│   │   ├── __init__.py
│   │   ├── model.py
│   │   ├── poser.py
│   │   └── versions.py
│   ├── problems.py
│   ├── templates/
│   │   └── mail/
│   │   └── warn_about_foo.html.mako
│   ├── _version.py
│   └── web/
│   ├── __init__.py
│   ├── app.py
│   ├── static/
│   │   ├── __init__.py
│   │   ├── css/
│   │   ├── favicon.ico
│   │   ├── img/
│   │   └── js/
│   ├── subscribers.py
│   ├── templates/
│   │   ├── base.mako
│   │   ├── batch/
│   │   │   └── foobatch/
│   │   ├── customers/
│   │   ├── menu.mako
│   │   └── products/
│   └── views/
│   ├── __init__.py
│   ├── batch/
│   │   ├── __init__.py
│   │   └── foobatch.py
│   ├── common.py
│   ├── customers.py
│   └── products.py
├── README.rst
└── setup.py

39
fabfile.py vendored
View file

@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Fabric script for Tailbone
"""
from __future__ import unicode_literals, absolute_import
import shutil
from fabric.api import task, local
@task
def release():
"""
Release a new version of 'Tailbone'.
"""
shutil.rmtree('Tailbone.egg-info')
local('python setup.py sdist --formats=gztar upload')

103
pyproject.toml Normal file
View file

@ -0,0 +1,103 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "Tailbone"
version = "0.22.3"
description = "Backoffice Web Application for Rattail"
readme = "README.md"
authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
license = {text = "GNU GPL v3+"}
classifiers = [
"Development Status :: 4 - Beta",
"Environment :: Web Environment",
"Framework :: Pyramid",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
"Natural Language :: English",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Office/Business",
"Topic :: Software Development :: Libraries :: Python Modules",
]
requires-python = ">= 3.8"
dependencies = [
"asgiref",
"colander",
"ColanderAlchemy",
"cornice",
"cornice-swagger",
"deform",
"humanize",
"Mako",
"markdown",
"openpyxl",
"paginate",
"paginate_sqlalchemy",
"passlib",
"Pillow",
"pyramid>=2",
"pyramid_beaker",
"pyramid_deform",
"pyramid_exclog",
"pyramid_fanstatic",
"pyramid_mako",
"pyramid_retry",
"pyramid_tm",
"rattail[db,bouncer]>=0.18.5",
"sa-filters",
"simplejson",
"transaction",
"waitress",
"WebHelpers2",
"WuttaWeb>=0.14.0",
"zope.sqlalchemy>=1.5",
]
[project.optional-dependencies]
docs = ["Sphinx", "furo"]
tests = ["coverage", "mock", "pytest", "pytest-cov"]
[project.entry-points."paste.app_factory"]
main = "tailbone.app:main"
webapi = "tailbone.webapi:main"
[project.entry-points."rattail.cleaners"]
beaker = "tailbone.cleanup:BeakerCleaner"
[project.entry-points."rattail.config.extensions"]
tailbone = "tailbone.config:ConfigExtension"
[project.urls]
Homepage = "https://rattailproject.org"
Repository = "https://forgejo.wuttaproject.org/rattail/tailbone"
Issues = "https://forgejo.wuttaproject.org/rattail/tailbone/issues"
Changelog = "https://forgejo.wuttaproject.org/rattail/tailbone/src/branch/master/CHANGELOG.md"
[tool.commitizen]
version_provider = "pep621"
tag_format = "v$version"
update_changelog_on_bump = true
[tool.nosetests]
nocapture = 1
cover-package = "tailbone"
cover-erase = 1
cover-html = 1
cover-html-dir = "htmlcov"

View file

@ -1,6 +0,0 @@
[nosetests]
nocapture = 1
cover-package = tailbone
cover-erase = 1
cover-html = 1
cover-html-dir = htmlcov

173
setup.py
View file

@ -1,173 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Setup script for Tailbone
"""
from __future__ import unicode_literals, absolute_import
import os.path
from setuptools import setup, find_packages
here = os.path.abspath(os.path.dirname(__file__))
execfile(os.path.join(here, 'tailbone', '_version.py'))
README = open(os.path.join(here, 'README.rst')).read()
requires = [
#
# Version numbers within comments below have specific meanings.
# Basically the 'low' value is a "soft low," and 'high' a "soft high."
# In other words:
#
# If either a 'low' or 'high' value exists, the primary point to be
# made about the value is that it represents the most current (stable)
# version available for the package (assuming typical public access
# methods) whenever this project was started and/or documented.
# Therefore:
#
# If a 'low' version is present, you should know that attempts to use
# versions of the package significantly older than the 'low' version
# may not yield happy results. (A "hard" high limit may or may not be
# indicated by a true version requirement.)
#
# Similarly, if a 'high' version is present, and especially if this
# project has laid dormant for a while, you may need to refactor a bit
# when attempting to support a more recent version of the package. (A
# "hard" low limit should be indicated by a true version requirement
# when a 'high' version is present.)
#
# In any case, developers and other users are encouraged to play
# outside the lines with regard to these soft limits. If bugs are
# encountered then they should be filed as such.
#
# package # low high
# For now, let's restrict FormEncode to 1.2 since the 1.3 release
# introduces some deprecation warnings. Once we're running 1.2 everywhere
# in production, we can start looking at adding 1.3 support.
# TODO: Remove this restriction.
'FormEncode<=1.2.99', # 1.2.4 1.2.6
# FormAlchemy 1.5 supports Python 3 but is being a little aggressive about
# it, for our needs...We'll have to stick with 1.4 for now.
u'FormAlchemy<=1.4.99', # 1.4.3
# Pyramid 1.3 introduced 'pcreate' command (and friends) to replace
# deprecated 'paster create' (and friends).
'pyramid>=1.3a1', # 1.3b2 1.4.5
'humanize', # 0.5.1
'Mako', # 0.6.2
'pyramid_beaker>=0.6', # 0.6.1
'pyramid_debugtoolbar', # 1.0
'pyramid_exclog', # 0.6
'pyramid_mako', # 1.0.2
'pyramid_simpleform', # 0.6.1
'pyramid_tm', # 0.3
'rattail[db,auth,bouncer]', # 0.5.0
'six', # 1.10.0
'transaction', # 1.2.0
'waitress', # 0.8.1
'WebHelpers', # 1.3
'WTForms', # 2.1
'zope.sqlalchemy', # 0.7
# TODO: Need to figure out what to do about this...
# # This is used to obtain POD image dimensions.
# 'PIL', # 1.1.7
]
extras = {
'docs': [
#
# package # low high
'Sphinx', # 1.2
],
'tests': [
#
# package # low high
'coverage', # 3.6
'fixture', # 1.5
'mock', # 1.0.1
'nose', # 1.3.0
],
}
setup(
name = "Tailbone",
version = __version__,
author = "Lance Edgar",
author_email = "lance@edbob.org",
url = "http://rattailproject.org/",
license = "GNU Affero GPL v3",
description = "Backoffice Web Application for Rattail",
long_description = README,
classifiers = [
'Development Status :: 3 - Alpha',
'Environment :: Web Environment',
'Framework :: Pyramid',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU Affero General Public License v3',
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Office/Business',
'Topic :: Software Development :: Libraries :: Python Modules',
],
install_requires = requires,
extras_require = extras,
tests_require = ['Tailbone[tests]'],
test_suite = 'nose.collector',
packages = find_packages(exclude=['tests.*', 'tests']),
include_package_data = True,
zip_safe = False,
entry_points = {
'paste.app_factory': [
'main = tailbone.app:main',
],
'rattail.config.extensions': [
'tailbone = tailbone.config:ConfigExtension',
],
'pyramid.scaffold': [
'rattail = tailbone.scaffolds:RattailTemplate',
],
},
)

View file

@ -1,24 +1,23 @@
#!/usr/bin/env python # -*- coding: utf-8; -*-
# -*- coding: utf-8 -*-
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2012 Lance Edgar # Copyright © 2010-2017 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
# Rattail is free software: you can redistribute it and/or modify it under the # Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free # terms of the GNU General Public License as published by the Free Software
# Software Foundation, either version 3 of the License, or (at your option) # Foundation, either version 3 of the License, or (at your option) any later
# any later version. # version.
# #
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY # Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# more details. # details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU General Public License along with
# along with Rattail. If not, see <http://www.gnu.org/licenses/>. # Rattail. If not, see <http://www.gnu.org/licenses/>.
# #
################################################################################ ################################################################################

View file

@ -1,3 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8; -*-
__version__ = u'0.5.83' try:
from importlib.metadata import version
except ImportError:
from importlib_metadata import version
__version__ = version('Tailbone')

40
tailbone/api/__init__.py Normal file
View file

@ -0,0 +1,40 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2022 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API
"""
from __future__ import unicode_literals, absolute_import
from .core import APIView, api
from .master import APIMasterView, SortColumn
# TODO: remove this
from .master2 import APIMasterView2
def includeme(config):
config.include('tailbone.api.common')
config.include('tailbone.api.auth')
config.include('tailbone.api.customers')
config.include('tailbone.api.upgrades')
config.include('tailbone.api.users')

229
tailbone/api/auth.py Normal file
View file

@ -0,0 +1,229 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Auth Views
"""
from cornice import Service
from tailbone.api import APIView, api
from tailbone.db import Session
from tailbone.auth import login_user, logout_user
class AuthenticationView(APIView):
@api
def check_session(self):
"""
View to serve as "no-op" / ping action to check current user's session.
This will establish a server-side web session for the user if none
exists. Note that this also resets the user's session timer.
"""
data = {'ok': True, 'permissions': []}
if self.request.user:
data['user'] = self.get_user_info(self.request.user)
data['permissions'] = list(self.request.user_permissions)
# background color may be set per-request, by some apps
if hasattr(self.request, 'background_color') and self.request.background_color:
data['background_color'] = self.request.background_color
else: # otherwise we use the one from config
data['background_color'] = self.rattail_config.get(
'tailbone', 'background_color')
# TODO: this seems the best place to return some global app
# settings, but maybe not desirable in all cases..in which
# case should caller need to ask for these explicitly? or
# make a different call altogether to get them..?
app = self.get_rattail_app()
customer_handler = app.get_clientele_handler()
data['settings'] = {
'customer_field_dropdown': customer_handler.choice_uses_dropdown(),
}
return data
@api
def login(self):
"""
API login view.
"""
if self.request.method == 'OPTIONS':
return self.request.response
username = self.request.json.get('username')
password = self.request.json.get('password')
if not (username and password):
return {'error': "Invalid username or password"}
# make sure credentials are valid
user = self.authenticate_user(username, password)
if not user:
return {'error': "Invalid username or password"}
# is there some reason this user should not login?
error = self.why_cant_user_login(user)
if error:
return {'error': error}
app = self.get_rattail_app()
auth = app.get_auth_handler()
login_user(self.request, user)
return {
'ok': True,
'user': self.get_user_info(user),
'permissions': list(auth.get_permissions(Session(), user)),
}
def authenticate_user(self, username, password):
app = self.get_rattail_app()
auth = app.get_auth_handler()
return auth.authenticate_user(Session(), username, password)
def why_cant_user_login(self, user):
"""
This method is given a ``User`` instance, which represents someone who
is just now trying to login, and has already cleared the basic hurdle
of providing the correct credentials for a user on file. This method
is responsible then, for further verification that this user *should*
in fact be allowed to login to this app node. If the method determines
a reason the user should *not* be allowed to login, then it should
return that reason as a simple string.
"""
@api
def logout(self):
"""
API logout view.
"""
if self.request.method == 'OPTIONS':
return self.request.response
logout_user(self.request)
return {'ok': True}
@api
def become_root(self):
"""
Elevate the current request to 'root' for full system access.
"""
if not self.request.is_admin:
raise self.forbidden()
self.request.user.record_event(self.enum.USER_EVENT_BECOME_ROOT)
self.request.session['is_root'] = True
return {
'ok': True,
'user': self.get_user_info(self.request.user),
}
@api
def stop_root(self):
"""
Lower the current request from 'root' back to normal access.
"""
if not self.request.is_admin:
raise self.forbidden()
self.request.user.record_event(self.enum.USER_EVENT_STOP_ROOT)
self.request.session['is_root'] = False
return {
'ok': True,
'user': self.get_user_info(self.request.user),
}
@api
def change_password(self):
"""
View which allows a user to change their password.
"""
if self.request.method == 'OPTIONS':
return self.request.response
if not self.request.user:
raise self.forbidden()
if self.request.user.prevent_password_change and not self.request.is_root:
raise self.forbidden()
data = self.request.json_body
# first make sure "current" password is accurate
if not self.authenticate_user(self.request.user, data['current_password']):
return {'error': "The current/old password you provided is incorrect"}
# okay then, set new password
auth = self.app.get_auth_handler()
auth.set_user_password(self.request.user, data['new_password'])
return {
'ok': True,
'user': self.get_user_info(self.request.user),
}
@classmethod
def defaults(cls, config):
cls._auth_defaults(config)
@classmethod
def _auth_defaults(cls, config):
# session
check_session = Service(name='check_session', path='/session')
check_session.add_view('GET', 'check_session', klass=cls)
config.add_cornice_service(check_session)
# login
login = Service(name='login', path='/login')
login.add_view('POST', 'login', klass=cls)
config.add_cornice_service(login)
# logout
logout = Service(name='logout', path='/logout')
logout.add_view('POST', 'logout', klass=cls)
config.add_cornice_service(logout)
# become root
become_root = Service(name='become_root', path='/become-root')
become_root.add_view('POST', 'become_root', klass=cls)
config.add_cornice_service(become_root)
# stop root
stop_root = Service(name='stop_root', path='/stop-root')
stop_root.add_view('POST', 'stop_root', klass=cls)
config.add_cornice_service(stop_root)
# change password
change_password = Service(name='change_password', path='/change-password')
change_password.add_view('POST', 'change_password', klass=cls)
config.add_cornice_service(change_password)
def defaults(config, **kwargs):
base = globals()
AuthenticationView = kwargs.get('AuthenticationView', base['AuthenticationView'])
AuthenticationView.defaults(config)
def includeme(config):
defaults(config)

View file

@ -0,0 +1,29 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2019 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Batches
"""
from __future__ import unicode_literals, absolute_import
from .core import APIBatchView, APIBatchRowView, BatchAPIMasterView

360
tailbone/api/batch/core.py Normal file
View file

@ -0,0 +1,360 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Batch Views
"""
import logging
import warnings
from cornice import Service
from tailbone.api import APIMasterView
log = logging.getLogger(__name__)
class APIBatchMixin(object):
"""
Base class for all API views which are meant to handle "batch" *and/or*
"batch row" data.
"""
def get_batch_class(self):
model_class = self.get_model_class()
if hasattr(model_class, '__batch_class__'):
return model_class.__batch_class__
return model_class
def get_handler(self):
"""
Returns a `BatchHandler` instance for the view. All (?) custom batch
API views should define a default handler class; however this may in all
(?) cases be overridden by config also. The specific setting required
to do so will depend on the 'key' for the type of batch involved, e.g.
assuming the 'vendor_catalog' batch:
.. code-block:: ini
[rattail.batch]
vendor_catalog.handler = myapp.batch.vendorcatalog:CustomCatalogHandler
Note that the 'key' for a batch is generally the same as its primary
table name, although technically it is whatever value returns from the
``batch_key`` attribute of the main batch model class.
"""
app = self.get_rattail_app()
key = self.get_batch_class().batch_key
return app.get_batch_handler(key, default=self.default_handler_spec)
class APIBatchView(APIBatchMixin, APIMasterView):
"""
Base class for all API views which are meant to handle "batch" *and/or*
"batch row" data.
"""
supports_toggle_complete = False
supports_execute = False
def __init__(self, request, **kwargs):
super(APIBatchView, self).__init__(request, **kwargs)
self.batch_handler = self.get_handler()
@property
def handler(self):
warnings.warn("the `handler` property is deprecated; "
"please use `batch_handler` instead",
DeprecationWarning, stacklevel=2)
return self.batch_handler
def normalize(self, batch):
app = self.get_rattail_app()
created = app.localtime(batch.created, from_utc=True)
executed = None
if batch.executed:
executed = app.localtime(batch.executed, from_utc=True)
return {
'uuid': batch.uuid,
'_str': str(batch),
'id': batch.id,
'id_str': batch.id_str,
'description': batch.description,
'notes': batch.notes,
'params': batch.params or {},
'rowcount': batch.rowcount,
'created': str(created),
'created_display': self.pretty_datetime(created),
'created_by_uuid': batch.created_by.uuid,
'created_by_display': str(batch.created_by),
'complete': batch.complete,
'status_code': batch.status_code,
'status_display': batch.STATUS.get(batch.status_code,
str(batch.status_code)),
'executed': str(executed) if executed else None,
'executed_display': self.pretty_datetime(executed) if executed else None,
'executed_by_uuid': batch.executed_by_uuid,
'executed_by_display': str(batch.executed_by or ''),
'mutable': self.batch_handler.is_mutable(batch),
}
def create_object(self, data):
"""
Create a new object instance and populate it with the given data.
Here we'll invoke the handler for actual batch creation, instead of
typical logic used for simple records.
"""
user = self.request.user
kwargs = dict(data)
kwargs['user'] = user
batch = self.batch_handler.make_batch(self.Session(), **kwargs)
if self.batch_handler.should_populate(batch):
self.batch_handler.do_populate(batch, user)
return batch
def update_object(self, batch, data):
"""
Logic for updating a main object record.
Here we want to make sure we set "created by" to the current user, when
creating a new batch.
"""
# we're only concerned with *new* batches here
if not batch.uuid:
# assign creator; initialize row count
batch.created_by_uuid = self.request.user.uuid
if batch.rowcount is None:
batch.rowcount = 0
# then go ahead with usual logic
return super(APIBatchView, self).update_object(batch, data)
def mark_complete(self):
"""
Mark the given batch as "complete".
"""
batch = self.get_object()
if batch.executed:
return {'error': "Batch {} has already been executed: {}".format(
batch.id_str, batch.description)}
if batch.complete:
return {'error': "Batch {} is already marked complete: {}".format(
batch.id_str, batch.description)}
batch.complete = True
return self._get(obj=batch)
def mark_incomplete(self):
"""
Mark the given batch as "incomplete".
"""
batch = self.get_object()
if batch.executed:
return {'error': "Batch {} has already been executed: {}".format(
batch.id_str, batch.description)}
if not batch.complete:
return {'error': "Batch {} is already marked incomplete: {}".format(
batch.id_str, batch.description)}
batch.complete = False
return self._get(obj=batch)
def execute(self):
"""
Execute the given batch.
"""
batch = self.get_object()
if batch.executed:
return {'error': "Batch {} has already been executed: {}".format(
batch.id_str, batch.description)}
kwargs = dict(self.request.json_body)
kwargs.pop('user', None)
kwargs.pop('progress', None)
result = self.batch_handler.do_execute(batch, self.request.user, **kwargs)
return {'ok': bool(result), 'batch': self.normalize(batch)}
@classmethod
def defaults(cls, config):
cls._defaults(config)
cls._batch_defaults(config)
@classmethod
def _batch_defaults(cls, config):
route_prefix = cls.get_route_prefix()
permission_prefix = cls.get_permission_prefix()
collection_url_prefix = cls.get_collection_url_prefix()
object_url_prefix = cls.get_object_url_prefix()
if cls.supports_toggle_complete:
# mark complete
mark_complete = Service(name='{}.mark_complete'.format(route_prefix),
path='{}/{{uuid}}/mark-complete'.format(object_url_prefix))
mark_complete.add_view('POST', 'mark_complete', klass=cls,
permission='{}.edit'.format(permission_prefix))
config.add_cornice_service(mark_complete)
# mark incomplete
mark_incomplete = Service(name='{}.mark_incomplete'.format(route_prefix),
path='{}/{{uuid}}/mark-incomplete'.format(object_url_prefix))
mark_incomplete.add_view('POST', 'mark_incomplete', klass=cls,
permission='{}.edit'.format(permission_prefix))
config.add_cornice_service(mark_incomplete)
if cls.supports_execute:
# execute batch
execute = Service(name='{}.execute'.format(route_prefix),
path='{}/{{uuid}}/execute'.format(object_url_prefix))
execute.add_view('POST', 'execute', klass=cls,
permission='{}.execute'.format(permission_prefix))
config.add_cornice_service(execute)
# TODO: deprecate / remove this
BatchAPIMasterView = APIBatchView
class APIBatchRowView(APIBatchMixin, APIMasterView):
"""
Base class for all API views which are meant to handle "batch rows" data.
"""
editable = False
supports_quick_entry = False
def __init__(self, request, **kwargs):
super(APIBatchRowView, self).__init__(request, **kwargs)
self.batch_handler = self.get_handler()
@property
def handler(self):
warnings.warn("the `handler` property is deprecated; "
"please use `batch_handler` instead",
DeprecationWarning, stacklevel=2)
return self.batch_handler
def normalize(self, row):
batch = row.batch
return {
'uuid': row.uuid,
'_str': str(row),
'_parent_str': str(batch),
'_parent_uuid': batch.uuid,
'batch_uuid': batch.uuid,
'batch_id': batch.id,
'batch_id_str': batch.id_str,
'batch_description': batch.description,
'batch_complete': batch.complete,
'batch_executed': bool(batch.executed),
'batch_mutable': self.batch_handler.is_mutable(batch),
'sequence': row.sequence,
'status_code': row.status_code,
'status_display': row.STATUS.get(row.status_code, str(row.status_code)),
}
def update_object(self, row, data):
"""
Supplements the default logic as follows:
Invokes the batch handler's ``refresh_row()`` method after updating the
row's field data per usual.
"""
if not self.batch_handler.is_mutable(row.batch):
return {'error': "Batch is not mutable"}
# update row per usual
row = super(APIBatchRowView, self).update_object(row, data)
# okay now we apply handler refresh logic
self.batch_handler.refresh_row(row)
return row
def delete_object(self, row):
"""
Overrides the default logic as follows:
Delegates deletion of the row to the batch handler.
"""
self.batch_handler.do_remove_row(row)
def quick_entry(self):
"""
View for handling "quick entry" user input, for a batch.
"""
data = self.request.json_body
uuid = data['batch_uuid']
batch = self.Session.get(self.get_batch_class(), uuid)
if not batch:
raise self.notfound()
entry = data['quick_entry']
try:
row = self.batch_handler.quick_entry(self.Session(), batch, entry)
except Exception as error:
log.warning("quick entry failed for '%s' batch %s: %s",
self.batch_handler.batch_key, batch.id_str, entry,
exc_info=True)
msg = str(error)
if not msg and isinstance(error, NotImplementedError):
msg = "Feature is not implemented"
return {'error': msg}
if not row:
return {'error': "Could not identify product"}
self.Session.flush()
result = self._get(obj=row)
result['ok'] = True
return result
@classmethod
def defaults(cls, config):
cls._defaults(config)
cls._batch_row_defaults(config)
@classmethod
def _batch_row_defaults(cls, config):
route_prefix = cls.get_route_prefix()
permission_prefix = cls.get_permission_prefix()
collection_url_prefix = cls.get_collection_url_prefix()
if cls.supports_quick_entry:
# quick entry
quick_entry = Service(name='{}.quick_entry'.format(route_prefix),
path='{}/quick-entry'.format(collection_url_prefix))
quick_entry.add_view('POST', 'quick_entry', klass=cls,
permission='{}.edit'.format(permission_prefix))
config.add_cornice_service(quick_entry)

View file

@ -0,0 +1,200 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Inventory Batches
"""
import decimal
import sqlalchemy as sa
from rattail import pod
from rattail.db.model import InventoryBatch, InventoryBatchRow
from cornice import Service
from tailbone.api.batch import APIBatchView, APIBatchRowView
class InventoryBatchViews(APIBatchView):
model_class = InventoryBatch
default_handler_spec = 'rattail.batch.inventory:InventoryBatchHandler'
route_prefix = 'inventory'
permission_prefix = 'batch.inventory'
collection_url_prefix = '/inventory-batches'
object_url_prefix = '/inventory-batch'
supports_toggle_complete = True
def normalize(self, batch):
data = super().normalize(batch)
data['mode'] = batch.mode
data['mode_display'] = self.enum.INVENTORY_MODE.get(batch.mode)
if data['mode_display'] is None and batch.mode is not None:
data['mode_display'] = str(batch.mode)
data['reason_code'] = batch.reason_code
return data
def count_modes(self):
"""
Retrieve info about the available batch count modes.
"""
permission_prefix = self.get_permission_prefix()
if self.request.is_root:
modes = self.batch_handler.get_count_modes()
else:
modes = self.batch_handler.get_allowed_count_modes(
self.Session(), self.request.user,
permission_prefix=permission_prefix)
return modes
def adjustment_reasons(self):
"""
Retrieve info about the available "reasons" for inventory adjustment
batches.
"""
raw_reasons = self.batch_handler.get_adjustment_reasons(self.Session())
reasons = []
for reason in raw_reasons:
reasons.append({
'uuid': reason.uuid,
'code': reason.code,
'description': reason.description,
'hidden': reason.hidden,
})
return reasons
@classmethod
def defaults(cls, config):
cls._defaults(config)
cls._batch_defaults(config)
cls._inventory_defaults(config)
@classmethod
def _inventory_defaults(cls, config):
route_prefix = cls.get_route_prefix()
permission_prefix = cls.get_permission_prefix()
collection_url_prefix = cls.get_collection_url_prefix()
# get count modes
count_modes = Service(name='{}.count_modes'.format(route_prefix),
path='{}/count-modes'.format(collection_url_prefix))
count_modes.add_view('GET', 'count_modes', klass=cls,
permission='{}.list'.format(permission_prefix))
config.add_cornice_service(count_modes)
# get adjustment reasons
adjustment_reasons = Service(name='{}.adjustment_reasons'.format(route_prefix),
path='{}/adjustment-reasons'.format(collection_url_prefix))
adjustment_reasons.add_view('GET', 'adjustment_reasons', klass=cls,
permission='{}.list'.format(permission_prefix))
config.add_cornice_service(adjustment_reasons)
class InventoryBatchRowViews(APIBatchRowView):
model_class = InventoryBatchRow
default_handler_spec = 'rattail.batch.inventory:InventoryBatchHandler'
route_prefix = 'inventory.rows'
permission_prefix = 'batch.inventory'
collection_url_prefix = '/inventory-batch-rows'
object_url_prefix = '/inventory-batch-row'
editable = True
supports_quick_entry = True
def normalize(self, row):
batch = row.batch
data = super().normalize(row)
app = self.get_rattail_app()
data['item_id'] = row.item_id
data['upc'] = str(row.upc)
data['upc_pretty'] = row.upc.pretty() if row.upc else None
data['brand_name'] = row.brand_name
data['description'] = row.description
data['size'] = row.size
data['full_description'] = row.product.full_description if row.product else row.description
data['image_url'] = pod.get_image_url(self.rattail_config, row.upc) if row.upc else None
data['case_quantity'] = app.render_quantity(row.case_quantity or 1)
data['cases'] = row.cases
data['units'] = row.units
data['unit_uom'] = 'LB' if row.product and row.product.weighed else 'EA'
data['quantity_display'] = "{} {}".format(
app.render_quantity(row.cases or row.units),
'CS' if row.cases else data['unit_uom'])
data['allow_cases'] = self.batch_handler.allow_cases(batch)
return data
def update_object(self, row, data):
"""
Supplements the default logic as follows:
Converts certain fields within the data, to proper "native" types.
"""
data = dict(data)
# convert some data types as needed
if 'cases' in data:
if data['cases'] == '':
data['cases'] = None
elif data['cases']:
data['cases'] = decimal.Decimal(data['cases'])
if 'units' in data:
if data['units'] == '':
data['units'] = None
elif data['units']:
data['units'] = decimal.Decimal(data['units'])
# update row per usual
try:
row = super().update_object(row, data)
except sa.exc.DataError as error:
# detect when user scans barcode for cases/units field
if hasattr(error, 'orig'):
orig = type(error.orig)
if hasattr(orig, '__name__'):
# nb. this particular error is from psycopg2
if orig.__name__ == 'NumericValueOutOfRange':
return {'error': "Numeric value out of range"}
raise
return row
def defaults(config, **kwargs):
base = globals()
InventoryBatchViews = kwargs.get('InventoryBatchViews', base['InventoryBatchViews'])
InventoryBatchViews.defaults(config)
InventoryBatchRowViews = kwargs.get('InventoryBatchRowViews', base['InventoryBatchRowViews'])
InventoryBatchRowViews.defaults(config)
def includeme(config):
defaults(config)

View file

@ -0,0 +1,78 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Label Batches
"""
from rattail.db import model
from tailbone.api.batch import APIBatchView, APIBatchRowView
class LabelBatchViews(APIBatchView):
model_class = model.LabelBatch
default_handler_spec = 'rattail.batch.labels:LabelBatchHandler'
route_prefix = 'labelbatchviews'
permission_prefix = 'labels.batch'
collection_url_prefix = '/label-batches'
object_url_prefix = '/label-batch'
supports_toggle_complete = True
class LabelBatchRowViews(APIBatchRowView):
model_class = model.LabelBatchRow
default_handler_spec = 'rattail.batch.labels:LabelBatchHandler'
route_prefix = 'api.label_batch_rows'
permission_prefix = 'labels.batch'
collection_url_prefix = '/label-batch-rows'
object_url_prefix = '/label-batch-row'
supports_quick_entry = True
def normalize(self, row):
batch = row.batch
data = super().normalize(row)
data['item_id'] = row.item_id
data['upc'] = str(row.upc)
data['upc_pretty'] = row.upc.pretty() if row.upc else None
data['brand_name'] = row.brand_name
data['description'] = row.description
data['size'] = row.size
data['full_description'] = row.product.full_description if row.product else row.description
return data
def defaults(config, **kwargs):
base = globals()
LabelBatchViews = kwargs.get('LabelBatchViews', base['LabelBatchViews'])
LabelBatchViews.defaults(config)
LabelBatchRowViews = kwargs.get('LabelBatchRowViews', base['LabelBatchRowViews'])
LabelBatchRowViews.defaults(config)
def includeme(config):
defaults(config)

View file

@ -0,0 +1,318 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Ordering Batches
These views expose the basic CRUD interface to "ordering" batches, for the web
API.
"""
import datetime
import logging
import sqlalchemy as sa
from rattail.db.model import PurchaseBatch, PurchaseBatchRow
from cornice import Service
from tailbone.api.batch import APIBatchView, APIBatchRowView
log = logging.getLogger(__name__)
class OrderingBatchViews(APIBatchView):
model_class = PurchaseBatch
default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler'
route_prefix = 'orderingbatchviews'
permission_prefix = 'ordering'
collection_url_prefix = '/ordering-batches'
object_url_prefix = '/ordering-batch'
supports_toggle_complete = True
supports_execute = True
def base_query(self):
"""
Modifies the default logic as follows:
Adds a condition to the query, to ensure only purchase batches with
"ordering" mode are returned.
"""
model = self.model
query = super().base_query()
query = query.filter(model.PurchaseBatch.mode == self.enum.PURCHASE_BATCH_MODE_ORDERING)
return query
def normalize(self, batch):
data = super().normalize(batch)
data['vendor_uuid'] = batch.vendor.uuid
data['vendor_display'] = str(batch.vendor)
data['department_uuid'] = batch.department_uuid
data['department_display'] = str(batch.department) if batch.department else None
data['po_total_calculated_display'] = "${:0.2f}".format(batch.po_total_calculated or 0)
data['ship_method'] = batch.ship_method
data['notes_to_vendor'] = batch.notes_to_vendor
return data
def create_object(self, data):
"""
Modifies the default logic as follows:
Sets the mode to "ordering" for the new batch.
"""
data = dict(data)
if not data.get('vendor_uuid'):
raise ValueError("You must specify the vendor")
data['mode'] = self.enum.PURCHASE_BATCH_MODE_ORDERING
batch = super().create_object(data)
return batch
def worksheet(self):
"""
Returns primary data for the Ordering Worksheet view.
"""
batch = self.get_object()
if batch.executed:
raise self.forbidden()
app = self.get_rattail_app()
# TODO: much of the logic below was copied from the traditional master
# view for ordering batches. should maybe let them share it somehow?
# organize existing batch rows by product
order_items = {}
for row in batch.active_rows():
order_items[row.product_uuid] = row
# organize vendor catalog costs by dept / subdept
departments = {}
costs = self.batch_handler.get_order_form_costs(self.Session(), batch.vendor)
costs = self.batch_handler.sort_order_form_costs(costs)
costs = list(costs) # we must have a stable list for the rest of this
self.batch_handler.decorate_order_form_costs(batch, costs)
for cost in costs:
department = cost.product.department
if department:
department_dict = departments.setdefault(department.uuid, {
'uuid': department.uuid,
'number': department.number,
'name': department.name,
})
else:
if None not in departments:
departments[None] = {
'uuid': None,
'number': None,
'name': "",
}
department_dict = departments[None]
subdepartments = department_dict.setdefault('subdepartments', {})
subdepartment = cost.product.subdepartment
if subdepartment:
subdepartment_dict = subdepartments.setdefault(subdepartment.uuid, {
'uuid': subdepartment.uuid,
'number': subdepartment.number,
'name': subdepartment.name,
})
else:
if None not in subdepartments:
subdepartments[None] = {
'uuid': None,
'number': None,
'name': "",
}
subdepartment_dict = subdepartments[None]
subdept_costs = subdepartment_dict.setdefault('costs', [])
product = cost.product
subdept_costs.append({
'uuid': cost.uuid,
'upc': str(product.upc),
'upc_pretty': product.upc.pretty() if product.upc else None,
'brand_name': product.brand.name if product.brand else None,
'description': product.description,
'size': product.size,
'case_size': cost.case_size,
'uom_display': "LB" if product.weighed else "EA",
'vendor_item_code': cost.code,
'preference': cost.preference,
'preferred': cost.preference == 1,
'unit_cost': cost.unit_cost,
'unit_cost_display': "${:0.2f}".format(cost.unit_cost) if cost.unit_cost is not None else "",
# TODO
# 'cases_ordered': None,
# 'units_ordered': None,
# 'po_total': None,
# 'po_total_display': None,
})
# sort the (sub)department groupings
sorted_departments = []
for dept in sorted(departments.values(), key=lambda d: d['name']):
dept['subdepartments'] = sorted(dept['subdepartments'].values(),
key=lambda s: s['name'])
sorted_departments.append(dept)
# fetch recent purchase history, sort/pad for template convenience
history = self.batch_handler.get_order_form_history(batch, costs, 6)
for i in range(6 - len(history)):
history.append(None)
history = list(reversed(history))
# must convert some date objects to string, for JSON sake
for h in history:
if not h:
continue
purchase = h.get('purchase')
if purchase:
dt = purchase.get('date_ordered')
if dt and isinstance(dt, datetime.date):
purchase['date_ordered'] = app.render_date(dt)
dt = purchase.get('date_received')
if dt and isinstance(dt, datetime.date):
purchase['date_received'] = app.render_date(dt)
return {
'batch': self.normalize(batch),
'departments': departments,
'sorted_departments': sorted_departments,
'history': history,
}
@classmethod
def defaults(cls, config):
cls._defaults(config)
cls._batch_defaults(config)
cls._ordering_batch_defaults(config)
@classmethod
def _ordering_batch_defaults(cls, config):
route_prefix = cls.get_route_prefix()
permission_prefix = cls.get_permission_prefix()
object_url_prefix = cls.get_object_url_prefix()
# worksheet
worksheet = Service(name='{}.worksheet'.format(route_prefix),
path='{}/{{uuid}}/worksheet'.format(object_url_prefix))
worksheet.add_view('GET', 'worksheet', klass=cls,
permission='{}.worksheet'.format(permission_prefix))
config.add_cornice_service(worksheet)
class OrderingBatchRowViews(APIBatchRowView):
model_class = PurchaseBatchRow
default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler'
route_prefix = 'ordering.rows'
permission_prefix = 'ordering'
collection_url_prefix = '/ordering-batch-rows'
object_url_prefix = '/ordering-batch-row'
supports_quick_entry = True
editable = True
def normalize(self, row):
data = super().normalize(row)
app = self.get_rattail_app()
batch = row.batch
data['item_id'] = row.item_id
data['upc'] = str(row.upc)
data['upc_pretty'] = row.upc.pretty() if row.upc else None
data['brand_name'] = row.brand_name
data['description'] = row.description
data['size'] = row.size
data['full_description'] = row.product.full_description if row.product else row.description
# # only provide image url if so configured
# if self.rattail_config.getbool('rattail.batch', 'purchase.mobile_images', default=True):
# data['image_url'] = pod.get_image_url(self.rattail_config, row.upc) if row.upc else None
# unit_uom can vary by product
data['unit_uom'] = 'LB' if row.product and row.product.weighed else 'EA'
data['case_quantity'] = row.case_quantity
data['cases_ordered'] = row.cases_ordered
data['units_ordered'] = row.units_ordered
data['cases_ordered_display'] = app.render_quantity(row.cases_ordered or 0, empty_zero=False)
data['units_ordered_display'] = app.render_quantity(row.units_ordered or 0, empty_zero=False)
data['po_unit_cost'] = row.po_unit_cost
data['po_unit_cost_display'] = "${:0.2f}".format(row.po_unit_cost) if row.po_unit_cost is not None else None
data['po_total_calculated'] = row.po_total_calculated
data['po_total_calculated_display'] = "${:0.2f}".format(row.po_total_calculated) if row.po_total_calculated is not None else None
data['status_code'] = row.status_code
data['status_display'] = row.STATUS.get(row.status_code, str(row.status_code))
return data
def update_object(self, row, data):
"""
Overrides the default logic as follows:
So far, we only allow updating the ``cases_ordered`` and/or
``units_ordered`` quantities; therefore ``data`` should have one or
both of those keys.
This data is then passed to the
:meth:`~rattail:rattail.batch.purchase.PurchaseBatchHandler.update_row_quantity()`
method of the batch handler.
Note that the "normal" logic for this method is not invoked at all.
"""
if not self.batch_handler.is_mutable(row.batch):
return {'error': "Batch is not mutable"}
try:
self.batch_handler.update_row_quantity(row, **data)
self.Session.flush()
except Exception as error:
log.warning("update_row_quantity failed", exc_info=True)
if isinstance(error, sa.exc.DataError) and hasattr(error, 'orig'):
error = str(error.orig)
else:
error = str(error)
return {'error': error}
return row
def defaults(config, **kwargs):
base = globals()
OrderingBatchViews = kwargs.get('OrderingBatchViews', base['OrderingBatchViews'])
OrderingBatchViews.defaults(config)
OrderingBatchRowViews = kwargs.get('OrderingBatchRowViews', base['OrderingBatchRowViews'])
OrderingBatchRowViews.defaults(config)
def includeme(config):
defaults(config)

View file

@ -0,0 +1,492 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Receiving Batches
"""
import logging
import humanize
import sqlalchemy as sa
from rattail.db.model import PurchaseBatch, PurchaseBatchRow
from cornice import Service
from deform import widget as dfwidget
from tailbone import forms
from tailbone.api.batch import APIBatchView, APIBatchRowView
from tailbone.forms.receiving import ReceiveRow
log = logging.getLogger(__name__)
class ReceivingBatchViews(APIBatchView):
model_class = PurchaseBatch
default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler'
route_prefix = 'receivingbatchviews'
permission_prefix = 'receiving'
collection_url_prefix = '/receiving-batches'
object_url_prefix = '/receiving-batch'
supports_toggle_complete = True
supports_execute = True
def base_query(self):
model = self.app.model
query = super().base_query()
query = query.filter(model.PurchaseBatch.mode == self.enum.PURCHASE_BATCH_MODE_RECEIVING)
return query
def normalize(self, batch):
data = super().normalize(batch)
data['vendor_uuid'] = batch.vendor.uuid
data['vendor_display'] = str(batch.vendor)
data['department_uuid'] = batch.department_uuid
data['department_display'] = str(batch.department) if batch.department else None
data['po_number'] = batch.po_number
data['po_total'] = batch.po_total
data['invoice_total'] = batch.invoice_total
data['invoice_total_calculated'] = batch.invoice_total_calculated
data['can_auto_receive'] = self.batch_handler.can_auto_receive(batch)
return data
def create_object(self, data):
data = dict(data)
# all about receiving mode here
data['mode'] = self.enum.PURCHASE_BATCH_MODE_RECEIVING
# assume "receive from PO" if given a PO key
if data.get('purchase_key'):
data['workflow'] = 'from_po'
return super().create_object(data)
def auto_receive(self):
"""
View which handles auto-marking as received, all items within
a pending batch.
"""
batch = self.get_object()
self.batch_handler.auto_receive_all_items(batch)
return self._get(obj=batch)
def mark_receiving_complete(self):
"""
Mark the given batch as "receiving complete".
"""
batch = self.get_object()
if batch.executed:
return {'error': "Batch {} has already been executed: {}".format(
batch.id_str, batch.description)}
if batch.complete:
return {'error': "Batch {} is already marked complete: {}".format(
batch.id_str, batch.description)}
if batch.receiving_complete:
return {'error': "Receiving is already complete for batch {}: {}".format(
batch.id_str, batch.description)}
batch.receiving_complete = True
return self._get(obj=batch)
def eligible_purchases(self):
model = self.app.model
uuid = self.request.params.get('vendor_uuid')
vendor = self.Session.get(model.Vendor, uuid) if uuid else None
if not vendor:
return {'error': "Vendor not found"}
purchases = self.batch_handler.get_eligible_purchases(
vendor, self.enum.PURCHASE_BATCH_MODE_RECEIVING)
purchases = [self.normalize_eligible_purchase(p)
for p in purchases]
return {'purchases': purchases}
def normalize_eligible_purchase(self, purchase):
return self.batch_handler.normalize_eligible_purchase(purchase)
def render_eligible_purchase(self, purchase):
return self.batch_handler.render_eligible_purchase(purchase)
@classmethod
def defaults(cls, config):
cls._defaults(config)
cls._batch_defaults(config)
cls._receiving_batch_defaults(config)
@classmethod
def _receiving_batch_defaults(cls, config):
route_prefix = cls.get_route_prefix()
permission_prefix = cls.get_permission_prefix()
collection_url_prefix = cls.get_collection_url_prefix()
object_url_prefix = cls.get_object_url_prefix()
# auto_receive
auto_receive = Service(name='{}.auto_receive'.format(route_prefix),
path='{}/{{uuid}}/auto-receive'.format(object_url_prefix))
auto_receive.add_view('GET', 'auto_receive', klass=cls,
permission='{}.auto_receive'.format(permission_prefix))
config.add_cornice_service(auto_receive)
# mark_receiving_complete
mark_receiving_complete = Service(name='{}.mark_receiving_complete'.format(route_prefix),
path='{}/{{uuid}}/mark-receiving-complete'.format(object_url_prefix))
mark_receiving_complete.add_view('POST', 'mark_receiving_complete', klass=cls,
permission='{}.edit'.format(permission_prefix))
config.add_cornice_service(mark_receiving_complete)
# eligible purchases
eligible_purchases = Service(name='{}.eligible_purchases'.format(route_prefix),
path='{}/eligible-purchases'.format(collection_url_prefix))
eligible_purchases.add_view('GET', 'eligible_purchases', klass=cls,
permission='{}.create'.format(permission_prefix))
config.add_cornice_service(eligible_purchases)
class ReceivingBatchRowViews(APIBatchRowView):
model_class = PurchaseBatchRow
default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler'
route_prefix = 'receiving.rows'
permission_prefix = 'receiving'
collection_url_prefix = '/receiving-batch-rows'
object_url_prefix = '/receiving-batch-row'
supports_quick_entry = True
def make_filter_spec(self):
model = self.app.model
filters = super().make_filter_spec()
if filters:
# must translate certain convenience filters
orig_filters, filters = filters, []
for filtr in orig_filters:
# # is_received
# # NOTE: this is only relevant for truck dump or "from scratch"
# if filtr['field'] == 'is_received' and filtr['op'] == 'eq' and filtr['value'] is True:
# filters.extend([
# {'or': [
# {'field': 'cases_received', 'op': '!=', 'value': 0},
# {'field': 'units_received', 'op': '!=', 'value': 0},
# ]},
# ])
# is_incomplete
if filtr['field'] == 'is_incomplete' and filtr['op'] == 'eq' and filtr['value'] is True:
# looking for any rows with "ordered" quantity, but where the
# status does *not* signify a "settled" row so to speak
# TODO: would be nice if we had a simple flag to leverage?
filters.extend([
{'or': [
{'field': 'cases_ordered', 'op': '!=', 'value': 0},
{'field': 'units_ordered', 'op': '!=', 'value': 0},
]},
{'field': 'status_code', 'op': 'not_in', 'value': [
model.PurchaseBatchRow.STATUS_OK,
model.PurchaseBatchRow.STATUS_PRODUCT_NOT_FOUND,
model.PurchaseBatchRow.STATUS_CASE_QUANTITY_DIFFERS,
]},
])
# is_invalid
elif filtr['field'] == 'is_invalid' and filtr['op'] == 'eq' and filtr['value'] is True:
filters.extend([
{'field': 'status_code', 'op': 'in', 'value': [
model.PurchaseBatchRow.STATUS_PRODUCT_NOT_FOUND,
model.PurchaseBatchRow.STATUS_COST_NOT_FOUND,
model.PurchaseBatchRow.STATUS_CASE_QUANTITY_UNKNOWN,
model.PurchaseBatchRow.STATUS_CASE_QUANTITY_DIFFERS,
]},
])
# is_unexpected
elif filtr['field'] == 'is_unexpected' and filtr['op'] == 'eq' and filtr['value'] is True:
# looking for any rows which do *not* have "ordered/shipped" quantity
filters.extend([
{'and': [
{'or': [
{'field': 'cases_ordered', 'op': 'is_null'},
{'field': 'cases_ordered', 'op': '==', 'value': 0},
]},
{'or': [
{'field': 'units_ordered', 'op': 'is_null'},
{'field': 'units_ordered', 'op': '==', 'value': 0},
]},
{'or': [
{'field': 'cases_shipped', 'op': 'is_null'},
{'field': 'cases_shipped', 'op': '==', 'value': 0},
]},
{'or': [
{'field': 'units_shipped', 'op': 'is_null'},
{'field': 'units_shipped', 'op': '==', 'value': 0},
]},
{'or': [
# but "unexpected" also implies we have some confirmed amount(s)
{'field': 'cases_received', 'op': '!=', 'value': 0},
{'field': 'units_received', 'op': '!=', 'value': 0},
{'field': 'cases_damaged', 'op': '!=', 'value': 0},
{'field': 'units_damaged', 'op': '!=', 'value': 0},
{'field': 'cases_expired', 'op': '!=', 'value': 0},
{'field': 'units_expired', 'op': '!=', 'value': 0},
]},
]},
])
# is_damaged
elif filtr['field'] == 'is_damaged' and filtr['op'] == 'eq' and filtr['value'] is True:
filters.extend([
{'or': [
{'field': 'cases_damaged', 'op': '!=', 'value': 0},
{'field': 'units_damaged', 'op': '!=', 'value': 0},
]},
])
# is_expired
elif filtr['field'] == 'is_expired' and filtr['op'] == 'eq' and filtr['value'] is True:
filters.extend([
{'or': [
{'field': 'cases_expired', 'op': '!=', 'value': 0},
{'field': 'units_expired', 'op': '!=', 'value': 0},
]},
])
# is_missing
elif filtr['field'] == 'is_missing' and filtr['op'] == 'eq' and filtr['value'] is True:
filters.extend([
{'or': [
{'field': 'cases_missing', 'op': '!=', 'value': 0},
{'field': 'units_missing', 'op': '!=', 'value': 0},
]},
])
else: # just some filter, use as-is
filters.append(filtr)
return filters
def normalize(self, row):
data = super().normalize(row)
model = self.app.model
batch = row.batch
prodder = self.app.get_products_handler()
data['product_uuid'] = row.product_uuid
data['item_id'] = row.item_id
data['upc'] = str(row.upc)
data['upc_pretty'] = row.upc.pretty() if row.upc else None
data['brand_name'] = row.brand_name
data['description'] = row.description
data['size'] = row.size
data['full_description'] = row.product.full_description if row.product else row.description
# only provide image url if so configured
if self.rattail_config.getbool('rattail.batch', 'purchase.mobile_images', default=True):
data['image_url'] = prodder.get_image_url(product=row.product, upc=row.upc)
# unit_uom can vary by product
data['unit_uom'] = 'LB' if row.product and row.product.weighed else 'EA'
data['case_quantity'] = row.case_quantity
data['order_quantities_known'] = batch.order_quantities_known
data['cases_ordered'] = row.cases_ordered
data['units_ordered'] = row.units_ordered
data['cases_shipped'] = row.cases_shipped
data['units_shipped'] = row.units_shipped
data['cases_received'] = row.cases_received
data['units_received'] = row.units_received
data['cases_damaged'] = row.cases_damaged
data['units_damaged'] = row.units_damaged
data['cases_expired'] = row.cases_expired
data['units_expired'] = row.units_expired
data['cases_missing'] = row.cases_missing
data['units_missing'] = row.units_missing
cases, units = self.batch_handler.get_unconfirmed_counts(row)
data['cases_unconfirmed'] = cases
data['units_unconfirmed'] = units
data['po_unit_cost'] = row.po_unit_cost
data['po_total'] = row.po_total
data['invoice_number'] = row.invoice_number
data['invoice_unit_cost'] = row.invoice_unit_cost
data['invoice_total'] = row.invoice_total
data['invoice_total_calculated'] = row.invoice_total_calculated
data['allow_cases'] = self.batch_handler.allow_cases()
data['quick_receive'] = self.rattail_config.getbool(
'rattail.batch', 'purchase.mobile_quick_receive',
default=True)
if batch.order_quantities_known:
data['quick_receive_all'] = self.rattail_config.getbool(
'rattail.batch', 'purchase.mobile_quick_receive_all',
default=False)
# TODO: this was copied from regular view receive_row() method; should merge
if data['quick_receive'] and data.get('quick_receive_all'):
if data['allow_cases']:
data['quick_receive_uom'] = 'CS'
raise NotImplementedError("TODO: add CS support for quick_receive_all")
else:
data['quick_receive_uom'] = data['unit_uom']
accounted_for = self.batch_handler.get_units_accounted_for(row)
remainder = self.batch_handler.get_units_ordered(row) - accounted_for
if accounted_for:
# some product accounted for; button should receive "remainder" only
if remainder:
remainder = self.app.render_quantity(remainder)
data['quick_receive_quantity'] = remainder
data['quick_receive_text'] = "Receive Remainder ({} {})".format(
remainder, data['unit_uom'])
else:
# unless there is no remainder, in which case disable it
data['quick_receive'] = False
else: # nothing yet accounted for, button should receive "all"
if not remainder:
log.warning("quick receive remainder is empty for row %s", row.uuid)
remainder = self.app.render_quantity(remainder)
data['quick_receive_quantity'] = remainder
data['quick_receive_text'] = "Receive ALL ({} {})".format(
remainder, data['unit_uom'])
data['unexpected_alert'] = None
if batch.order_quantities_known and not row.cases_ordered and not row.units_ordered:
warn = True
if batch.is_truck_dump_parent() and row.product:
uuids = [child.uuid for child in batch.truck_dump_children]
if uuids:
count = self.Session.query(model.PurchaseBatchRow)\
.filter(model.PurchaseBatchRow.batch_uuid.in_(uuids))\
.filter(model.PurchaseBatchRow.product == row.product)\
.count()
if count:
warn = False
if warn:
data['unexpected_alert'] = "This item was NOT on the original purchase order."
# TODO: surely the caller of API should determine this flag?
# maybe alert user if they've already received some of this product
alert_received = self.rattail_config.getbool('tailbone', 'receiving.alert_already_received',
default=False)
if alert_received:
data['received_alert'] = None
if self.batch_handler.get_units_confirmed(row):
msg = "You have already received some of this product; last update was {}.".format(
humanize.naturaltime(self.app.make_utc() - row.modified))
data['received_alert'] = msg
return data
def receive(self):
"""
View which handles "receiving" against a particular batch row.
"""
model = self.app.model
# first do basic input validation
schema = ReceiveRow().bind(session=self.Session())
form = forms.Form(schema=schema, request=self.request)
# TODO: this seems hacky, but avoids "complex" date value parsing
form.set_widget('expiration_date', dfwidget.TextInputWidget())
if not form.validate():
log.warning("form did not validate: %s",
form.make_deform_form().error)
return {'error': "Form did not validate"}
# fetch / validate row object
row = self.Session.get(model.PurchaseBatchRow, form.validated['row'])
if row is not self.get_object():
return {'error': "Specified row does not match the route!"}
# handler takes care of the row receiving logic for us
kwargs = dict(form.validated)
del kwargs['row']
try:
self.batch_handler.receive_row(row, **kwargs)
self.Session.flush()
except Exception as error:
log.warning("receive() failed", exc_info=True)
if isinstance(error, sa.exc.DataError) and hasattr(error, 'orig'):
error = str(error.orig)
else:
error = str(error)
return {'error': error}
return self._get(obj=row)
@classmethod
def defaults(cls, config):
cls._defaults(config)
cls._batch_row_defaults(config)
cls._receiving_batch_row_defaults(config)
@classmethod
def _receiving_batch_row_defaults(cls, config):
route_prefix = cls.get_route_prefix()
permission_prefix = cls.get_permission_prefix()
object_url_prefix = cls.get_object_url_prefix()
# receive (row)
receive = Service(name='{}.receive'.format(route_prefix),
path='{}/{{uuid}}/receive'.format(object_url_prefix))
receive.add_view('POST', 'receive', klass=cls,
permission='{}.edit_row'.format(permission_prefix))
config.add_cornice_service(receive)
def defaults(config, **kwargs):
base = globals()
ReceivingBatchViews = kwargs.get('ReceivingBatchViews', base['ReceivingBatchViews'])
ReceivingBatchViews.defaults(config)
ReceivingBatchRowViews = kwargs.get('ReceivingBatchRowViews', base['ReceivingBatchRowViews'])
ReceivingBatchRowViews.defaults(config)
def includeme(config):
defaults(config)

159
tailbone/api/common.py Normal file
View file

@ -0,0 +1,159 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - "Common" Views
"""
from collections import OrderedDict
from rattail.util import get_pkg_version
from cornice import Service
from cornice.service import get_services
from cornice_swagger import CorniceSwagger
from tailbone import forms
from tailbone.forms.common import Feedback
from tailbone.api import APIView, api
from tailbone.db import Session
class CommonView(APIView):
"""
Misc. "common" views for the API.
.. attribute:: feedback_email_key
This is the email key which will be used when sending "user feedback"
email. Default value is ``'user_feedback'``.
"""
feedback_email_key = 'user_feedback'
@api
def about(self):
"""
Generic view to show "about project" info page.
"""
packages = self.get_packages()
return {
'project_title': self.get_project_title(),
'project_version': self.get_project_version(),
'packages': packages,
'package_names': list(packages),
}
def get_project_title(self):
app = self.get_rattail_app()
return app.get_title()
def get_project_version(self):
app = self.get_rattail_app()
return app.get_version()
def get_packages(self):
"""
Should return the full set of packages which should be displayed on the
'about' page.
"""
return OrderedDict([
('rattail', get_pkg_version('rattail')),
('Tailbone', get_pkg_version('Tailbone')),
])
@api
def feedback(self):
"""
View to handle user feedback form submits.
"""
app = self.get_rattail_app()
model = self.model
# TODO: this logic was copied from tailbone.views.common and is largely
# identical; perhaps should merge somehow?
schema = Feedback().bind(session=Session())
form = forms.Form(schema=schema, request=self.request)
if form.validate():
data = dict(form.validated)
# figure out who the sending user is, if any
if self.request.user:
data['user'] = self.request.user
elif data['user']:
data['user'] = Session.get(model.User, data['user'])
# TODO: should provide URL to view user
if data['user']:
data['user_url'] = '#' # TODO: could get from config?
data['client_ip'] = self.request.client_addr
email_key = data['email_key'] or self.feedback_email_key
app.send_email(email_key, data=data)
return {'ok': True}
return {'error': "Form did not validate!"}
def swagger(self):
doc = CorniceSwagger(get_services())
app = self.get_rattail_app()
spec = doc.generate(f"{app.get_node_title()} API docs",
app.get_version(),
base_path='/api') # TODO
return spec
@classmethod
def defaults(cls, config):
cls._common_defaults(config)
@classmethod
def _common_defaults(cls, config):
rattail_config = config.registry.settings.get('rattail_config')
app = rattail_config.get_app()
# about
about = Service(name='about', path='/about')
about.add_view('GET', 'about', klass=cls)
config.add_cornice_service(about)
# feedback
feedback = Service(name='feedback', path='/feedback')
feedback.add_view('POST', 'feedback', klass=cls,
permission='common.feedback')
config.add_cornice_service(feedback)
# swagger
swagger = Service(name='swagger',
path='/swagger.json',
description=f"OpenAPI documentation for {app.get_title()}")
swagger.add_view('GET', 'swagger', klass=cls,
permission='common.api_swagger')
config.add_cornice_service(swagger)
def defaults(config, **kwargs):
base = globals()
CommonView = kwargs.get('CommonView', base['CommonView'])
CommonView.defaults(config)
def includeme(config):
defaults(config)

125
tailbone/api/core.py Normal file
View file

@ -0,0 +1,125 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Core Views
"""
from tailbone.views import View
def api(view_meth):
"""
Common decorator for all API views. Ideally this would not be needed..but
for now, alas, it is.
"""
def wrapped(view, *args, **kwargs):
# TODO: why doesn't this work here...? (instead we have to repeat this
# code in lots of other places)
# if view.request.method == 'OPTIONS':
# return view.request.response
# invoke the view logic first, since presumably it may involve a
# redirect in which case we don't really need to add the CSRF token.
# main known use case for this is the /logout endpoint - if that gets
# hit then the "current" (old) session will be destroyed, in which case
# we can't use the token from that, but instead must generate a new one.
result = view_meth(view, *args, **kwargs)
# explicitly set CSRF token cookie, unless OPTIONS request
# TODO: why doesn't pyramid do this for us again?
if view.request.method != 'OPTIONS':
view.request.response.set_cookie(name='XSRF-TOKEN',
value=view.request.session.get_csrf_token())
return result
return wrapped
class APIView(View):
"""
Base class for all API views.
"""
def pretty_datetime(self, dt):
if not dt:
return ""
return dt.strftime('%Y-%m-%d @ %I:%M %p')
def get_user_info(self, user):
"""
This method is present on *all* API views, and is meant to provide a
single means of obtaining "common" user info, for return to the caller.
Such info may be returned in several places, e.g. upon login but also
in the "check session" call, or e.g. as part of a broader return value
from any other call.
:returns: Dictionary of user info data, ready for JSON serialization.
Note that you should *not* (usually) override this method in any view,
but instead configure a "supplemental" function which can then add or
replace info entries. Config for that looks like e.g.:
.. code-block:: ini
[tailbone.api]
extra_user_info = poser.web.api.util:extra_user_info
Note that the above config assumes a simple *function* defined in your
``util`` module; such a function would look like e.g.::
def extra_user_info(request, user, **info):
# add favorite color
info['favorite_color'] = 'green'
# override display name
info['display_name'] = "TODO"
# remove short_name
info.pop('short_name', None)
return info
"""
app = self.get_rattail_app()
auth = app.get_auth_handler()
# basic / default info
is_admin = auth.user_is_admin(user)
employee = app.get_employee(user)
info = {
'uuid': user.uuid,
'username': user.username,
'display_name': user.display_name,
'short_name': auth.get_short_display_name(user),
'is_admin': is_admin,
'is_root': is_admin and self.request.session.get('is_root', False),
'employee_uuid': employee.uuid if employee else None,
'email_address': app.get_contact_email_address(user),
}
# maybe get/use "extra" info
extra = self.rattail_config.get('tailbone.api', 'extra_user_info',
usedb=False)
if extra:
extra = app.load_object(extra)
info = extra(self.request, user, **info)
return info

60
tailbone/api/customers.py Normal file
View file

@ -0,0 +1,60 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Customer Views
"""
from rattail.db import model
from tailbone.api import APIMasterView
class CustomerView(APIMasterView):
"""
API views for Customer data
"""
model_class = model.Customer
collection_url_prefix = '/customers'
object_url_prefix = '/customer'
supports_autocomplete = True
autocomplete_fieldname = 'name'
def normalize(self, customer):
return {
'uuid': customer.uuid,
'_str': str(customer),
'id': customer.id,
'number': customer.number,
'name': customer.name,
}
def defaults(config, **kwargs):
base = globals()
CustomerView = kwargs.get('CustomerView', base['CustomerView'])
CustomerView.defaults(config)
def includeme(config):
defaults(config)

View file

@ -0,0 +1,36 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Essential views for convenient includes
"""
def defaults(config, **kwargs):
mod = lambda spec: kwargs.get(spec, spec)
config.include(mod('tailbone.api.auth'))
config.include(mod('tailbone.api.common'))
def includeme(config):
defaults(config)

51
tailbone/api/labels.py Normal file
View file

@ -0,0 +1,51 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Label Views
"""
from __future__ import unicode_literals, absolute_import
from rattail.db.model import LabelProfile
from tailbone.api import APIMasterView
class LabelProfileView(APIMasterView):
"""
API views for Label Profile data
"""
model_class = LabelProfile
collection_url_prefix = '/label-profiles'
object_url_prefix = '/label-profile'
def defaults(config, **kwargs):
base = globals()
LabelProfileView = kwargs.get('LabelProfileView', base['LabelProfileView'])
LabelProfileView.defaults(config)
def includeme(config):
defaults(config)

618
tailbone/api/master.py Normal file
View file

@ -0,0 +1,618 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Master View
"""
import json
from rattail.db.util import get_fieldnames
from cornice import resource, Service
from tailbone.api import APIView
from tailbone.db import Session
from tailbone.util import SortColumn
class APIMasterView(APIView):
"""
Base class for data model REST API views.
"""
listable = True
creatable = True
viewable = True
editable = True
deletable = True
supports_autocomplete = False
supports_download = False
supports_rawbytes = False
@property
def Session(self):
return Session
@classmethod
def get_model_class(cls):
if hasattr(cls, 'model_class'):
return cls.model_class
raise NotImplementedError("must set `model_class` for {}".format(cls.__name__))
@classmethod
def get_normalized_model_name(cls):
if hasattr(cls, 'normalized_model_name'):
return cls.normalized_model_name
return cls.get_model_class().__name__.lower()
@classmethod
def get_route_prefix(cls):
"""
Returns a prefix which (by default) applies to all routes provided by
this view class.
"""
prefix = getattr(cls, 'route_prefix', None)
if prefix:
return prefix
model_name = cls.get_normalized_model_name()
return '{}s'.format(model_name)
@classmethod
def get_permission_prefix(cls):
"""
Returns a prefix which (by default) applies to all permissions
leveraged by this view class.
"""
prefix = getattr(cls, 'permission_prefix', None)
if prefix:
return prefix
return cls.get_route_prefix()
@classmethod
def get_collection_url_prefix(cls):
"""
Returns a prefix which (by default) applies to all "collection" URLs
provided by this view class.
"""
prefix = getattr(cls, 'collection_url_prefix', None)
if prefix:
return prefix
return '/{}'.format(cls.get_route_prefix())
@classmethod
def get_object_url_prefix(cls):
"""
Returns a prefix which (by default) applies to all "object" URLs
provided by this view class.
"""
prefix = getattr(cls, 'object_url_prefix', None)
if prefix:
return prefix
return '/{}'.format(cls.get_route_prefix())
@classmethod
def get_object_key(cls):
if hasattr(cls, 'object_key'):
return cls.object_key
return cls.get_normalized_model_name()
@classmethod
def get_collection_key(cls):
if hasattr(cls, 'collection_key'):
return cls.collection_key
return '{}s'.format(cls.get_object_key())
@classmethod
def establish_method(cls, method_name):
"""
Establish the given HTTP method for this Cornice Resource.
Cornice will auto-register any class methods for a resource, if they
are named according to what it expects (i.e. 'get', 'collection_get'
etc.). Tailbone API tries to make things automagical for the sake of
e.g. Poser logic, but in this case if we predefine all of these methods
and then some subclass view wants to *not* allow one, it's not clear
how to "undefine" it per se. Or at least, the more straightforward
thing (I think) is to not define such a method in the first place, if
it was not wanted.
Enter ``establish_method()``, which is what finally "defines" each
resource method according to what the subclass has declared via its
various attributes (:attr:`creatable`, :attr:`deletable` etc.).
Note that you will not likely have any need to use this
``establish_method()`` yourself! But we describe its purpose here, for
clarity.
"""
def method(self):
internal_method = getattr(self, '_{}'.format(method_name))
return internal_method()
setattr(cls, method_name, method)
def make_filter_spec(self):
if not self.request.GET.has_key('filters'):
return []
filters = json.loads(self.request.GET.getone('filters'))
return filters
def make_sort_spec(self):
# we prefer a "native sort"
if self.request.GET.has_key('nativeSort'):
return json.loads(self.request.GET.getone('nativeSort'))
# these params are based on 'vuetable-2'
# https://www.vuetable.com/guide/sorting.html#initial-sorting-order
if 'sort' in self.request.params:
sort = self.request.params['sort']
sortkey, sortdir = sort.split('|')
if sortdir != 'desc':
sortdir = 'asc'
return [
{
# 'model': self.model_class.__name__,
'field': sortkey,
'direction': sortdir,
},
]
# these params are based on 'vue-tables-2'
# https://github.com/matfish2/vue-tables-2#server-side
if 'orderBy' in self.request.params and 'ascending' in self.request.params:
sortcol = self.interpret_sortcol(self.request.params['orderBy'])
if sortcol:
spec = {
'field': sortcol.field_name,
'direction': 'asc' if self.config.parse_bool(self.request.params['ascending']) else 'desc',
}
if sortcol.model_name:
spec['model'] = sortcol.model_name
return [spec]
def interpret_sortcol(self, order_by):
"""
This must return a ``SortColumn`` object based on parsing of the given
``order_by`` string, which is "raw" as received from the client.
Please override as necessary, but in all cases you should invoke
:meth:`sortcol()` to obtain your return value. Default behavior
for this method is to simply do (only) that::
return self.sortcol(order_by)
Note that you can also return ``None`` here, if the given ``order_by``
string does not represent a valid sort.
"""
return self.sortcol(order_by)
def sortcol(self, field_name, model_name=None):
"""
Return a simple ``SortColumn`` object which denotes the field and
optionally, the model, to be used when sorting.
"""
if not model_name:
model_name = self.model_class.__name__
return SortColumn(field_name, model_name)
def join_for_sort_spec(self, query, sort_spec):
"""
This should apply any joins needed on the given query, to accommodate
requested sorting as per ``sort_spec`` - which will be non-empty but
otherwise no claims are made regarding its contents.
Please override as necessary, but in all cases you should return a
query, either untouched or else with join(s) applied.
"""
model_name = sort_spec[0].get('model')
return self.join_for_sort_model(query, model_name)
def join_for_sort_model(self, query, model_name):
"""
This should apply any joins needed on the given query, to accommodate
requested sorting on a field associated with the given model.
Please override as necessary, but in all cases you should return a
query, either untouched or else with join(s) applied.
"""
return query
def make_pagination_spec(self):
# these params are based on 'vuetable-2'
# https://github.com/ratiw/vuetable-2-tutorial/wiki/prerequisite#sample-api-endpoint
if 'page' in self.request.params and 'per_page' in self.request.params:
page = self.request.params['page']
per_page = self.request.params['per_page']
if page.isdigit() and per_page.isdigit():
return int(page), int(per_page)
# these params are based on 'vue-tables-2'
# https://github.com/matfish2/vue-tables-2#server-side
if 'page' in self.request.params and 'limit' in self.request.params:
page = self.request.params['page']
limit = self.request.params['limit']
if page.isdigit() and limit.isdigit():
return int(page), int(limit)
def base_query(self):
cls = self.get_model_class()
query = self.Session.query(cls)
return query
def get_fieldnames(self):
if not hasattr(self, '_fieldnames'):
self._fieldnames = get_fieldnames(
self.rattail_config, self.model_class,
columns=True, proxies=True, relations=False)
return self._fieldnames
def normalize(self, obj):
data = {'_str': str(obj)}
for field in self.get_fieldnames():
data[field] = getattr(obj, field)
return data
def _collection_get(self):
from sa_filters import apply_filters, apply_sort, apply_pagination
query = self.base_query()
context = {}
# maybe filter query
filter_spec = self.make_filter_spec()
if filter_spec:
query = apply_filters(query, filter_spec)
# maybe sort query
sort_spec = self.make_sort_spec()
if sort_spec:
query = self.join_for_sort_spec(query, sort_spec)
query = apply_sort(query, sort_spec)
# maybe paginate query
pagination_spec = self.make_pagination_spec()
if pagination_spec:
number, size = pagination_spec
query, pagination = apply_pagination(query, page_number=number, page_size=size)
# these properties are based on 'vuetable-2'
# https://www.vuetable.com/guide/pagination.html#how-the-pagination-component-works
context['total'] = pagination.total_results
context['per_page'] = pagination.page_size
context['current_page'] = pagination.page_number
context['last_page'] = pagination.num_pages
context['from'] = pagination.page_size * (pagination.page_number - 1) + 1
to = pagination.page_size * (pagination.page_number - 1) + pagination.page_size
if to > pagination.total_results:
context['to'] = pagination.total_results
else:
context['to'] = to
# these properties are based on 'vue-tables-2'
# https://github.com/matfish2/vue-tables-2#server-side
context['count'] = pagination.total_results
objects = [self.normalize(obj) for obj in query]
# TODO: test this for ratbob!
context[self.get_collection_key()] = objects
# these properties are based on 'vue-tables-2'
# https://github.com/matfish2/vue-tables-2#server-side
context['data'] = objects
if 'count' not in context:
context['count'] = len(objects)
return context
def get_object(self, uuid=None):
if not uuid:
uuid = self.request.matchdict['uuid']
obj = self.Session.get(self.get_model_class(), uuid)
if obj:
return obj
raise self.notfound()
def _get(self, obj=None, uuid=None):
if not obj:
obj = self.get_object(uuid=uuid)
key = self.get_object_key()
normal = self.normalize(obj)
return {key: normal, 'data': normal}
def _collection_post(self):
"""
Default method for actually processing a POST request for the
collection, aka. "create new object".
"""
# assume our data comes only from request JSON body
data = self.request.json_body
# add instance to session, and return data for it
try:
obj = self.create_object(data)
except Exception as error:
return self.json_response({'error': str(error)})
else:
self.Session.flush()
return self._get(obj)
def create_object(self, data):
"""
Create a new object instance and populate it with the given data.
Note that this method by default will only populate *simple* fields, so
you may need to subclass and override to add more complex field logic.
"""
# create new instance of model class
cls = self.get_model_class()
obj = cls()
# "update" new object with given data
obj = self.update_object(obj, data)
# that's all we can do here, subclass must override if more needed
self.Session.add(obj)
return obj
def _post(self, uuid=None):
"""
Default method for actually processing a POST request for an object,
aka. "update existing object".
"""
if not uuid:
uuid = self.request.matchdict['uuid']
obj = self.Session.get(self.get_model_class(), uuid)
if not obj:
raise self.notfound()
# assume our data comes only from request JSON body
data = self.request.json_body
# try to update data for object, returning error as necessary
obj = self.update_object(obj, data)
if isinstance(obj, dict) and 'error' in obj:
return {'error': obj['error']}
# return data for object
self.Session.flush()
return self._get(obj)
def update_object(self, obj, data):
"""
Update the given object instance with the given data.
Note that this method by default will only update *simple* fields, so
you may need to subclass and override to add more complex field logic.
"""
# set values for simple fields only
for key, value in data.items():
if hasattr(obj, key):
# TODO: what about datetime, decimal etc.?
setattr(obj, key, value)
# that's all we can do here, subclass must override if more needed
return obj
##############################
# delete
##############################
def _delete(self):
"""
View to handle DELETE action for an existing record/object.
"""
obj = self.get_object()
self.delete_object(obj)
def delete_object(self, obj):
"""
Delete the object, or mark it as deleted, or whatever you need to do.
"""
# flush immediately to force any pending integrity errors etc.
self.Session.delete(obj)
self.Session.flush()
##############################
# download
##############################
def download(self):
"""
GET view allowing for download of a single file, which is attached to a
given record.
"""
obj = self.get_object()
filename = self.request.GET.get('filename', None)
if not filename:
raise self.notfound()
path = self.download_path(obj, filename)
response = self.file_response(path)
return response
def download_path(self, obj, filename):
"""
Should return absolute path on disk, for the given object and filename.
Result will be used to return a file response to client.
"""
raise NotImplementedError
def rawbytes(self):
"""
GET view allowing for direct access to the raw bytes of a file, which
is attached to a given record. Basically the same as 'download' except
this does not come as an attachment.
"""
obj = self.get_object()
# TODO: is this really needed?
# filename = self.request.GET.get('filename', None)
# if filename:
# path = self.download_path(obj, filename)
# return self.file_response(path, attachment=False)
return self.rawbytes_response(obj)
def rawbytes_response(self, obj):
raise NotImplementedError
##############################
# autocomplete
##############################
def autocomplete(self):
"""
View which accepts a single ``term`` param, and returns a list of
autocomplete results to match.
"""
term = self.request.params.get('term', '').strip()
term = self.prepare_autocomplete_term(term)
if not term:
return []
results = self.get_autocomplete_data(term)
return [{'label': self.autocomplete_display(x),
'value': self.autocomplete_value(x)}
for x in results]
@property
def autocomplete_fieldname(self):
raise NotImplementedError("You must define `autocomplete_fieldname` "
"attribute for API view class: {}".format(
self.__class__))
def autocomplete_display(self, obj):
return getattr(obj, self.autocomplete_fieldname)
def autocomplete_value(self, obj):
return obj.uuid
def get_autocomplete_data(self, term):
query = self.make_autocomplete_query(term)
return query.all()
def make_autocomplete_query(self, term):
model_class = self.get_model_class()
query = self.Session.query(model_class)
query = self.filter_autocomplete_query(query)
field = getattr(model_class, self.autocomplete_fieldname)
query = query.filter(field.ilike('%%%s%%' % term))\
.order_by(field)
return query
def filter_autocomplete_query(self, query):
return query
def prepare_autocomplete_term(self, term):
"""
If necessary, massage the incoming search term for use with the
autocomplete query.
"""
return term
@classmethod
def defaults(cls, config):
cls._defaults(config)
@classmethod
def _defaults(cls, config):
route_prefix = cls.get_route_prefix()
permission_prefix = cls.get_permission_prefix()
collection_url_prefix = cls.get_collection_url_prefix()
object_url_prefix = cls.get_object_url_prefix()
# first, the primary resource API
# list/search
if cls.listable:
cls.establish_method('collection_get')
resource.add_view(cls.collection_get, permission='{}.list'.format(permission_prefix))
# create
if cls.creatable:
cls.establish_method('collection_post')
if hasattr(cls, 'permission_to_create'):
permission = cls.permission_to_create
else:
permission = '{}.create'.format(permission_prefix)
resource.add_view(cls.collection_post, permission=permission)
# view
if cls.viewable:
cls.establish_method('get')
resource.add_view(cls.get, permission='{}.view'.format(permission_prefix))
# edit
if cls.editable:
cls.establish_method('post')
resource.add_view(cls.post, permission='{}.edit'.format(permission_prefix))
# delete
if cls.deletable:
cls.establish_method('delete')
resource.add_view(cls.delete, permission='{}.delete'.format(permission_prefix))
# register primary resource API via cornice
object_resource = resource.add_resource(
cls,
collection_path=collection_url_prefix,
# TODO: probably should allow for other (composite?) key fields
path='{}/{{uuid}}'.format(object_url_prefix))
config.add_cornice_resource(object_resource)
# now for some more "custom" things, which are still somewhat generic
# autocomplete
if cls.supports_autocomplete:
autocomplete = Service(name='{}.autocomplete'.format(route_prefix),
path='{}/autocomplete'.format(collection_url_prefix))
autocomplete.add_view('GET', 'autocomplete', klass=cls,
permission='{}.list'.format(permission_prefix))
config.add_cornice_service(autocomplete)
# download
if cls.supports_download:
download = Service(name='{}.download'.format(route_prefix),
# TODO: probably should allow for other (composite?) key fields
path='{}/{{uuid}}/download'.format(object_url_prefix))
download.add_view('GET', 'download', klass=cls,
permission='{}.download'.format(permission_prefix))
config.add_cornice_service(download)
# rawbytes
if cls.supports_rawbytes:
rawbytes = Service(name='{}.rawbytes'.format(route_prefix),
# TODO: probably should allow for other (composite?) key fields
path='{}/{{uuid}}/rawbytes'.format(object_url_prefix))
rawbytes.add_view('GET', 'rawbytes', klass=cls,
permission='{}.download'.format(permission_prefix))
config.add_cornice_service(rawbytes)

43
tailbone/api/master2.py Normal file
View file

@ -0,0 +1,43 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2022 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Master View (v2)
"""
from __future__ import unicode_literals, absolute_import
import warnings
from tailbone.api import APIMasterView
class APIMasterView2(APIMasterView):
"""
Base class for data model REST API views.
"""
def __init__(self, request, context=None):
warnings.warn("APIMasterView2 class is deprecated; please use "
"APIMasterView instead",
DeprecationWarning, stacklevel=2)
super(APIMasterView2, self).__init__(request, context=context)

59
tailbone/api/people.py Normal file
View file

@ -0,0 +1,59 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Person Views
"""
from rattail.db import model
from tailbone.api import APIMasterView
class PersonView(APIMasterView):
"""
API views for Person data
"""
model_class = model.Person
permission_prefix = 'people'
collection_url_prefix = '/people'
object_url_prefix = '/person'
def normalize(self, person):
return {
'uuid': person.uuid,
'_str': str(person),
'first_name': person.first_name,
'last_name': person.last_name,
'display_name': person.display_name,
}
def defaults(config, **kwargs):
base = globals()
PersonView = kwargs.get('PersonView', base['PersonView'])
PersonView.defaults(config)
def includeme(config):
defaults(config)

220
tailbone/api/products.py Normal file
View file

@ -0,0 +1,220 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Product Views
"""
import logging
import sqlalchemy as sa
from sqlalchemy import orm
from cornice import Service
from rattail.db import model
from tailbone.api import APIMasterView
log = logging.getLogger(__name__)
class ProductView(APIMasterView):
"""
API views for Product data
"""
model_class = model.Product
collection_url_prefix = '/products'
object_url_prefix = '/product'
supports_autocomplete = True
def __init__(self, request, context=None):
super(ProductView, self).__init__(request, context=context)
app = self.get_rattail_app()
self.products_handler = app.get_products_handler()
def normalize(self, product):
# get what we can from handler
data = self.products_handler.normalize_product(product, fields=[
'brand_name',
'full_description',
'department_name',
'unit_price_display',
'sale_price',
'sale_price_display',
'sale_ends',
'sale_ends_display',
'tpr_price',
'tpr_price_display',
'tpr_ends',
'tpr_ends_display',
'current_price',
'current_price_display',
'current_ends',
'current_ends_display',
'vendor_name',
'costs',
'image_url',
])
# but must supplement
cost = product.cost
data.update({
'upc': str(product.upc),
'scancode': product.scancode,
'item_id': product.item_id,
'item_type': product.item_type,
'status_code': product.status_code,
'default_unit_cost': cost.unit_cost if cost else None,
'default_unit_cost_display': "${:0.2f}".format(cost.unit_cost) if cost and cost.unit_cost is not None else None,
})
return data
def make_autocomplete_query(self, term):
query = self.Session.query(model.Product)\
.outerjoin(model.Brand)\
.filter(sa.or_(
model.Brand.name.ilike('%{}%'.format(term)),
model.Product.description.ilike('%{}%'.format(term))))
if not self.request.has_perm('products.view_deleted'):
query = query.filter(model.Product.deleted == False)
query = query.order_by(model.Brand.name,
model.Product.description)\
.options(orm.joinedload(model.Product.brand))
return query
def autocomplete_display(self, product):
return product.full_description
def quick_lookup(self):
"""
View for handling "quick lookup" user input, for index page.
"""
data = self.request.GET
entry = data['entry']
product = self.products_handler.locate_product_for_entry(self.Session(),
entry)
if not product:
return {'error': "Product not found"}
return {'ok': True,
'product': self.normalize(product)}
def label_profiles(self):
"""
Returns the set of label profiles available for use with
printing label for product.
"""
app = self.get_rattail_app()
label_handler = app.get_label_handler()
model = self.model
profiles = []
for profile in label_handler.get_label_profiles(self.Session()):
profiles.append({
'uuid': profile.uuid,
'description': profile.description,
})
return {'label_profiles': profiles}
def print_labels(self):
app = self.get_rattail_app()
label_handler = app.get_label_handler()
model = self.model
data = self.request.json_body
uuid = data.get('label_profile_uuid')
profile = self.Session.get(model.LabelProfile, uuid) if uuid else None
if not profile:
return {'error': "Label profile not found"}
uuid = data.get('product_uuid')
product = self.Session.get(model.Product, uuid) if uuid else None
if not product:
return {'error': "Product not found"}
try:
quantity = int(data.get('quantity'))
except:
return {'error': "Quantity must be integer"}
printer = label_handler.get_printer(profile)
if not printer:
return {'error': "Couldn't get printer from label profile"}
try:
printer.print_labels([({'product': product}, quantity)])
except Exception as error:
log.warning("error occurred while printing labels", exc_info=True)
return {'error': str(error)}
return {'ok': True}
@classmethod
def defaults(cls, config):
cls._defaults(config)
cls._product_defaults(config)
@classmethod
def _product_defaults(cls, config):
route_prefix = cls.get_route_prefix()
permission_prefix = cls.get_permission_prefix()
collection_url_prefix = cls.get_collection_url_prefix()
# quick lookup
quick_lookup = Service(name='{}.quick_lookup'.format(route_prefix),
path='{}/quick-lookup'.format(collection_url_prefix))
quick_lookup.add_view('GET', 'quick_lookup', klass=cls,
permission='{}.list'.format(permission_prefix))
config.add_cornice_service(quick_lookup)
# label profiles
label_profiles = Service(name=f'{route_prefix}.label_profiles',
path=f'{collection_url_prefix}/label-profiles')
label_profiles.add_view('GET', 'label_profiles', klass=cls,
permission=f'{permission_prefix}.print_labels')
config.add_cornice_service(label_profiles)
# print labels
print_labels = Service(name='{}.print_labels'.format(route_prefix),
path='{}/print-labels'.format(collection_url_prefix))
print_labels.add_view('POST', 'print_labels', klass=cls,
permission='{}.print_labels'.format(permission_prefix))
config.add_cornice_service(print_labels)
def defaults(config, **kwargs):
base = globals()
ProductView = kwargs.get('ProductView', base['ProductView'])
ProductView.defaults(config)
def includeme(config):
defaults(config)

64
tailbone/api/upgrades.py Normal file
View file

@ -0,0 +1,64 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Upgrade Views
"""
from rattail.db import model
from tailbone.api import APIMasterView
class UpgradeView(APIMasterView):
"""
REST API views for Upgrade model.
"""
model_class = model.Upgrade
collection_url_prefix = '/upgrades'
object_url_prefix = '/upgrades'
def normalize(self, upgrade):
data = {
'created': upgrade.created.isoformat(),
'description': upgrade.description,
'enabled': upgrade.enabled,
'executed': upgrade.executed.isoformat() if upgrade.executed else None,
# 'executed_by':
}
if upgrade.status_code is None:
data['status_code'] = None
else:
data['status_code'] = self.enum.UPGRADE_STATUS.get(upgrade.status_code,
str(upgrade.status_code))
return data
def defaults(config, **kwargs):
base = globals()
UpgradeView = kwargs.get('UpgradeView', base['UpgradeView'])
UpgradeView.defaults(config)
def includeme(config):
defaults(config)

71
tailbone/api/users.py Normal file
View file

@ -0,0 +1,71 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - User Views
"""
from rattail.db import model
from tailbone.api import APIMasterView
class UserView(APIMasterView):
"""
API views for User data
"""
model_class = model.User
collection_url_prefix = '/users'
object_url_prefix = '/user'
def normalize(self, user):
return {
'uuid': user.uuid,
'username': user.username,
'person_display_name': (user.person.display_name or '') if user.person else '',
'active': user.active,
}
def interpret_sortcol(self, order_by):
if order_by == 'person_display_name':
return self.sortcol('Person', 'display_name')
return self.sortcol(order_by)
def join_for_sort_model(self, query, model_name):
if model_name == 'Person':
query = query.outerjoin(model.Person)
return query
def update_object(self, user, data):
# TODO: should ensure prevent_password_change is respected
return super(UserView, self).update_object(user, data)
def defaults(config, **kwargs):
base = globals()
UserView = kwargs.get('UserView', base['UserView'])
UserView.defaults(config)
def includeme(config):
defaults(config)

57
tailbone/api/vendors.py Normal file
View file

@ -0,0 +1,57 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Vendor Views
"""
from rattail.db import model
from tailbone.api import APIMasterView
class VendorView(APIMasterView):
model_class = model.Vendor
collection_url_prefix = '/vendors'
object_url_prefix = '/vendor'
supports_autocomplete = True
autocomplete_fieldname = 'name'
def normalize(self, vendor):
return {
'uuid': vendor.uuid,
'_str': str(vendor),
'id': vendor.id,
'name': vendor.name,
}
def defaults(config, **kwargs):
base = globals()
VendorView = kwargs.get('VendorView', base['VendorView'])
VendorView.defaults(config)
def includeme(config):
defaults(config)

234
tailbone/api/workorders.py Normal file
View file

@ -0,0 +1,234 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Work Order Views
"""
import datetime
from rattail.db.model import WorkOrder
from cornice import Service
from tailbone.api import APIMasterView
class WorkOrderView(APIMasterView):
model_class = WorkOrder
collection_url_prefix = '/workorders'
object_url_prefix = '/workorder'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
app = self.get_rattail_app()
self.workorder_handler = app.get_workorder_handler()
def normalize(self, workorder):
data = super().normalize(workorder)
data.update({
'customer_name': workorder.customer.name,
'status_label': self.enum.WORKORDER_STATUS[workorder.status_code],
'date_submitted': str(workorder.date_submitted or ''),
'date_received': str(workorder.date_received or ''),
'date_released': str(workorder.date_released or ''),
'date_delivered': str(workorder.date_delivered or ''),
})
return data
def create_object(self, data):
# invoke the handler instead of normal API CRUD logic
workorder = self.workorder_handler.make_workorder(self.Session(), **data)
return workorder
def update_object(self, workorder, data):
date_fields = [
'date_submitted',
'date_received',
'date_released',
'date_delivered',
]
# coerce date field values to proper datetime.date objects
for field in date_fields:
if field in data:
if data[field] == '':
data[field] = None
elif not isinstance(data[field], datetime.date):
date = datetime.datetime.strptime(data[field], '%Y-%m-%d').date()
data[field] = date
# coerce status code value to proper integer
if 'status_code' in data:
data['status_code'] = int(data['status_code'])
return super().update_object(workorder, data)
def status_codes(self):
"""
Retrieve all info about possible work order status codes.
"""
return self.workorder_handler.status_codes()
def receive(self):
"""
Sets work order status to "received".
"""
workorder = self.get_object()
self.workorder_handler.receive(workorder)
self.Session.flush()
return self.normalize(workorder)
def await_estimate(self):
"""
Sets work order status to "awaiting estimate confirmation".
"""
workorder = self.get_object()
self.workorder_handler.await_estimate(workorder)
self.Session.flush()
return self.normalize(workorder)
def await_parts(self):
"""
Sets work order status to "awaiting parts".
"""
workorder = self.get_object()
self.workorder_handler.await_parts(workorder)
self.Session.flush()
return self.normalize(workorder)
def work_on_it(self):
"""
Sets work order status to "working on it".
"""
workorder = self.get_object()
self.workorder_handler.work_on_it(workorder)
self.Session.flush()
return self.normalize(workorder)
def release(self):
"""
Sets work order status to "released".
"""
workorder = self.get_object()
self.workorder_handler.release(workorder)
self.Session.flush()
return self.normalize(workorder)
def deliver(self):
"""
Sets work order status to "delivered".
"""
workorder = self.get_object()
self.workorder_handler.deliver(workorder)
self.Session.flush()
return self.normalize(workorder)
def cancel(self):
"""
Sets work order status to "canceled".
"""
workorder = self.get_object()
self.workorder_handler.cancel(workorder)
self.Session.flush()
return self.normalize(workorder)
@classmethod
def defaults(cls, config):
cls._defaults(config)
cls._workorder_defaults(config)
@classmethod
def _workorder_defaults(cls, config):
route_prefix = cls.get_route_prefix()
permission_prefix = cls.get_permission_prefix()
collection_url_prefix = cls.get_collection_url_prefix()
object_url_prefix = cls.get_object_url_prefix()
# status codes
status_codes = Service(name='{}.status_codes'.format(route_prefix),
path='{}/status-codes'.format(collection_url_prefix))
status_codes.add_view('GET', 'status_codes', klass=cls,
permission='{}.list'.format(permission_prefix))
config.add_cornice_service(status_codes)
# receive
receive = Service(name='{}.receive'.format(route_prefix),
path='{}/{{uuid}}/receive'.format(object_url_prefix))
receive.add_view('POST', 'receive', klass=cls,
permission='{}.edit'.format(permission_prefix))
config.add_cornice_service(receive)
# await estimate confirmation
await_estimate = Service(name='{}.await_estimate'.format(route_prefix),
path='{}/{{uuid}}/await-estimate'.format(object_url_prefix))
await_estimate.add_view('POST', 'await_estimate', klass=cls,
permission='{}.edit'.format(permission_prefix))
config.add_cornice_service(await_estimate)
# await parts
await_parts = Service(name='{}.await_parts'.format(route_prefix),
path='{}/{{uuid}}/await-parts'.format(object_url_prefix))
await_parts.add_view('POST', 'await_parts', klass=cls,
permission='{}.edit'.format(permission_prefix))
config.add_cornice_service(await_parts)
# work on it
work_on_it = Service(name='{}.work_on_it'.format(route_prefix),
path='{}/{{uuid}}/work-on-it'.format(object_url_prefix))
work_on_it.add_view('POST', 'work_on_it', klass=cls,
permission='{}.edit'.format(permission_prefix))
config.add_cornice_service(work_on_it)
# release
release = Service(name='{}.release'.format(route_prefix),
path='{}/{{uuid}}/release'.format(object_url_prefix))
release.add_view('POST', 'release', klass=cls,
permission='{}.edit'.format(permission_prefix))
config.add_cornice_service(release)
# deliver
deliver = Service(name='{}.deliver'.format(route_prefix),
path='{}/{{uuid}}/deliver'.format(object_url_prefix))
deliver.add_view('POST', 'deliver', klass=cls,
permission='{}.edit'.format(permission_prefix))
config.add_cornice_service(deliver)
# cancel
cancel = Service(name='{}.cancel'.format(route_prefix),
path='{}/{{uuid}}/cancel'.format(object_url_prefix))
cancel.add_view('POST', 'cancel', klass=cls,
permission='{}.edit'.format(permission_prefix))
config.add_cornice_service(cancel)
def defaults(config, **kwargs):
base = globals()
WorkOrderView = kwargs.get('WorkOrderView', base['WorkOrderView'])
WorkOrderView.defaults(config)
def includeme(config):
defaults(config)

View file

@ -1,51 +1,46 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8; -*-
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2017 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
# Rattail is free software: you can redistribute it and/or modify it under the # Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free # terms of the GNU General Public License as published by the Free Software
# Software Foundation, either version 3 of the License, or (at your option) # Foundation, either version 3 of the License, or (at your option) any later
# any later version. # version.
# #
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY # Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# more details. # details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU General Public License along with
# along with Rattail. If not, see <http://www.gnu.org/licenses/>. # Rattail. If not, see <http://www.gnu.org/licenses/>.
# #
################################################################################ ################################################################################
""" """
Application Entry Point Application Entry Point
""" """
from __future__ import unicode_literals, absolute_import
import os import os
import warnings
import sqlalchemy as sa from sqlalchemy.orm import sessionmaker, scoped_session
from wuttjamaican.util import parse_list
import rattail.db
from rattail.config import make_config from rattail.config import make_config
from rattail.exceptions import ConfigurationError from rattail.exceptions import ConfigurationError
from rattail.db.util import get_engines
from rattail.db.continuum import configure_versioning
from rattail.db.types import GPCType
import formalchemy as fa
from pyramid.config import Configurator from pyramid.config import Configurator
from pyramid.authentication import SessionAuthenticationPolicy from zope.sqlalchemy import register
import tailbone.db import tailbone.db
from tailbone.auth import TailboneAuthorizationPolicy from tailbone.auth import TailboneSecurityPolicy
from tailbone.forms import renderers from tailbone.config import csrf_token_name, csrf_header_name
from tailbone.forms.alchemy import TemplateEngine from tailbone.util import get_effective_theme, get_theme_template_path
from tailbone.providers import get_all_providers
def make_rattail_config(settings): def make_rattail_config(settings):
@ -59,43 +54,37 @@ def make_rattail_config(settings):
# available for web requests later # available for web requests later
path = settings.get('rattail.config') path = settings.get('rattail.config')
if not path or not os.path.exists(path): if not path or not os.path.exists(path):
path = settings.get('edbob.config') raise ConfigurationError("Please set 'rattail.config' in [app:main] section of config "
if not path or not os.path.exists(path): "to the path of your config file. Lame, but necessary.")
raise ConfigurationError("Please set 'rattail.config' in [app:main] section of config "
"to the path of your config file. Lame, but necessary.")
warnings.warn("[app:main] setting 'edbob.config' is deprecated; "
"please use 'rattail.config' setting instead",
DeprecationWarning)
rattail_config = make_config(path) rattail_config = make_config(path)
settings['rattail_config'] = rattail_config settings['rattail_config'] = rattail_config
rattail_config.configure_logging()
rattail_engines = settings.get('rattail_engines') # nb. this is for compaibility with wuttaweb
if not rattail_engines: settings['wutta_config'] = rattail_config
# Load all Rattail database engines from config, and store in settings # configure database sessions
# dict. This is necessary e.g. in the case of a host server, to have if hasattr(rattail_config, 'appdb_engine'):
# access to its subordinate store servers. tailbone.db.Session.configure(bind=rattail_config.appdb_engine)
rattail_engines = get_engines(rattail_config) if hasattr(rattail_config, 'trainwreck_engine'):
settings['rattail_engines'] = rattail_engines tailbone.db.TrainwreckSession.configure(bind=rattail_config.trainwreck_engine)
# Configure the database session classes. Note that most of the time we'll
# be using the Tailbone Session, but occasionally (e.g. within batch
# processing threads) we want the Rattail Session. The reason is that
# during normal request processing, the Tailbone Session is preferable as
# it includes Zope Transaction magic. Within an explicitly-spawned thread
# however, this is *not* desirable.
rattail.db.Session.configure(bind=rattail_engines['default'])
tailbone.db.Session.configure(bind=rattail_engines['default'])
if hasattr(rattail_config, 'tempmon_engine'): if hasattr(rattail_config, 'tempmon_engine'):
tailbone.db.TempmonSession.configure(bind=rattail_config.tempmon_engine) tailbone.db.TempmonSession.configure(bind=rattail_config.tempmon_engine)
# maybe set "future" behavior for SQLAlchemy
if rattail_config.getbool('rattail.db', 'sqlalchemy_future_mode', usedb=False):
tailbone.db.Session.configure(future=True)
# create session wrappers for each "extra" Trainwreck engine
for key, engine in rattail_config.trainwreck_engines.items():
if key != 'default':
Session = scoped_session(sessionmaker(bind=engine))
register(Session)
tailbone.db.ExtraTrainwreckSessions[key] = Session
# Make sure rattail config object uses our scoped session, to avoid # Make sure rattail config object uses our scoped session, to avoid
# unnecessary connections (and pooling limits). # unnecessary connections (and pooling limits).
rattail_config._session_factory = lambda: (tailbone.db.Session(), False) rattail_config._session_factory = lambda: (tailbone.db.Session(), False)
# Configure (or not) Continuum versioning.
configure_versioning(rattail_config)
return rattail_config return rattail_config
@ -105,7 +94,12 @@ def provide_postgresql_settings(settings):
this enables retrying transactions a second time, in an attempt to this enables retrying transactions a second time, in an attempt to
gracefully handle database restarts. gracefully handle database restarts.
""" """
settings.setdefault('tm.attempts', 2) try:
import pyramid_retry
except ImportError:
settings.setdefault('tm.attempts', 2)
else:
settings.setdefault('retry.attempts', 2)
class Root(dict): class Root(dict):
@ -119,47 +113,201 @@ class Root(dict):
self.request = request self.request = request
def make_pyramid_config(settings): def make_pyramid_config(settings, configure_csrf=True):
""" """
Make a Pyramid config object from the given settings. Make a Pyramid config object from the given settings.
""" """
config = Configurator(settings=settings, root_factory=Root) rattail_config = settings['rattail_config']
# Configure user authentication / authorization. config = settings.pop('pyramid_config', None)
config.set_authentication_policy(SessionAuthenticationPolicy()) if config:
config.set_authorization_policy(TailboneAuthorizationPolicy()) config.set_root_factory(Root)
else:
# always require CSRF token protection # declare this web app of the "classic" variety
config.set_default_csrf_options(require_csrf=True, token='_csrf') settings.setdefault('tailbone.classic', 'true')
# we want the new themes feature!
establish_theme(settings)
settings.setdefault('fanstatic.versioning', 'true')
settings.setdefault('pyramid_deform.template_search_path', 'tailbone:templates/deform')
config = Configurator(settings=settings, root_factory=Root)
# add rattail config directly to registry, for access throughout the app
config.registry['rattail_config'] = rattail_config
# configure user authorization / authentication
config.set_security_policy(TailboneSecurityPolicy())
# maybe require CSRF token protection
if configure_csrf:
config.set_default_csrf_options(require_csrf=True,
token=csrf_token_name(rattail_config),
header=csrf_header_name(rattail_config))
# Bring in some Pyramid goodies. # Bring in some Pyramid goodies.
config.include('tailbone.beaker') config.include('tailbone.beaker')
config.include('pyramid_deform')
config.include('pyramid_fanstatic')
config.include('pyramid_mako') config.include('pyramid_mako')
config.include('pyramid_tm') config.include('pyramid_tm')
# Add some permissions magic. # TODO: this may be a good idea some day, if wanting to leverage
config.add_directive('add_tailbone_permission_group', 'tailbone.auth.add_permission_group') # deform resources for component JS? cf. also base.mako template
config.add_directive('add_tailbone_permission', 'tailbone.auth.add_permission') # # override default script mapping for deform
# from deform import Field
# from deform.widget import ResourceRegistry, default_resources
# registry = ResourceRegistry(use_defaults=False)
# for key in default_resources:
# registry.set_js_resources(key, None, {'js': []})
# Field.set_default_resource_registry(registry)
# TODO: This can finally be removed once all CRUD/index views have been # bring in the pyramid_retry logic, if available
# converted to use the new master view etc. # TODO: pretty soon we can require this package, hopefully..
for label, perms in settings.get('edbob.permissions', []): try:
groupkey = label.lower().replace(' ', '_') import pyramid_retry
config.add_tailbone_permission_group(groupkey, label) except ImportError:
for key, label in perms: pass
config.add_tailbone_permission(groupkey, key, label) else:
config.include('pyramid_retry')
# Configure FormAlchemy. # fetch all tailbone providers
fa.config.engine = TemplateEngine() providers = get_all_providers(rattail_config)
fa.FieldSet.default_renderers[sa.Boolean] = renderers.YesNoFieldRenderer for provider in providers.values():
fa.FieldSet.default_renderers[sa.Date] = renderers.DateFieldRenderer
fa.FieldSet.default_renderers[sa.DateTime] = renderers.DateTimeFieldRenderer # configure DB sessions associated with transaction manager
fa.FieldSet.default_renderers[sa.Time] = renderers.TimeFieldRenderer provider.configure_db_sessions(rattail_config, config)
fa.FieldSet.default_renderers[GPCType] = renderers.GPCFieldRenderer
# add any static includes
includes = provider.get_static_includes()
if includes:
for spec in includes:
config.include(spec)
# add some permissions magic
config.add_directive('add_wutta_permission_group',
'wuttaweb.auth.add_permission_group')
config.add_directive('add_wutta_permission',
'wuttaweb.auth.add_permission')
# TODO: deprecate / remove these
config.add_directive('add_tailbone_permission_group',
'wuttaweb.auth.add_permission_group')
config.add_directive('add_tailbone_permission',
'wuttaweb.auth.add_permission')
# and some similar magic for certain master views
config.add_directive('add_tailbone_index_page', 'tailbone.app.add_index_page')
config.add_directive('add_tailbone_config_page', 'tailbone.app.add_config_page')
config.add_directive('add_tailbone_model_view', 'tailbone.app.add_model_view')
config.add_directive('add_tailbone_view_supplement', 'tailbone.app.add_view_supplement')
config.add_directive('add_tailbone_websocket', 'tailbone.app.add_websocket')
return config return config
def add_websocket(config, name, view, attr=None):
"""
Register a websocket entry point for the app.
"""
def action():
rattail_config = config.registry.settings['rattail_config']
rattail_app = rattail_config.get_app()
if isinstance(view, str):
view_callable = rattail_app.load_object(view)
else:
view_callable = view
view_callable = view_callable(config)
if attr:
view_callable = getattr(view_callable, attr)
# register route
path = '/ws/{}'.format(name)
route_name = 'ws.{}'.format(name)
config.add_route(route_name, path, static=True)
# register view callable
websockets = config.registry.setdefault('tailbone_websockets', {})
websockets[path] = view_callable
config.action('tailbone-add-websocket-{}'.format(name), action,
# nb. since this action adds routes, it must happen
# sooner in the order than it normally would, hence
# we declare that
order=-20)
def add_index_page(config, route_name, label, permission):
"""
Register a config page for the app.
"""
def action():
pages = config.get_settings().get('tailbone_index_pages', [])
pages.append({'label': label, 'route': route_name,
'permission': permission})
config.add_settings({'tailbone_index_pages': pages})
config.action(None, action)
def add_config_page(config, route_name, label, permission):
"""
Register a config page for the app.
"""
def action():
pages = config.get_settings().get('tailbone_config_pages', [])
pages.append({'label': label, 'route': route_name,
'permission': permission})
config.add_settings({'tailbone_config_pages': pages})
config.action(None, action)
def add_model_view(config, model_name, label, route_prefix, permission_prefix):
"""
Register a model view for the app.
"""
def action():
all_views = config.get_settings().get('tailbone_model_views', {})
model_views = all_views.setdefault(model_name, [])
model_views.append({
'label': label,
'route_prefix': route_prefix,
'permission_prefix': permission_prefix,
})
config.add_settings({'tailbone_model_views': all_views})
config.action(None, action)
def add_view_supplement(config, route_prefix, cls):
"""
Register a master view supplement for the app.
"""
def action():
supplements = config.get_settings().get('tailbone_view_supplements', {})
supplements.setdefault(route_prefix, []).append(cls)
config.add_settings({'tailbone_view_supplements': supplements})
config.action(None, action)
def establish_theme(settings):
rattail_config = settings['rattail_config']
theme = get_effective_theme(rattail_config)
settings['tailbone.theme'] = theme
directories = settings['mako.directories']
if isinstance(directories, str):
directories = parse_list(directories)
path = get_theme_template_path(rattail_config)
directories.insert(0, path)
settings['mako.directories'] = directories
def configure_postgresql(pyramid_config): def configure_postgresql(pyramid_config):
""" """
Add some PostgreSQL-specific tweaks to the final app config. Specifically, Add some PostgreSQL-specific tweaks to the final app config. Specifically,
@ -173,7 +321,8 @@ def main(global_config, **settings):
""" """
This function returns a Pyramid WSGI application. This function returns a Pyramid WSGI application.
""" """
settings.setdefault('mako.directories', ['tailbone:templates']) settings.setdefault('mako.directories', ['tailbone:templates',
'wuttaweb:templates'])
rattail_config = make_rattail_config(settings) rattail_config = make_rattail_config(settings)
pyramid_config = make_pyramid_config(settings) pyramid_config = make_pyramid_config(settings)
pyramid_config.include('tailbone') pyramid_config.include('tailbone')

110
tailbone/asgi.py Normal file
View file

@ -0,0 +1,110 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
ASGI App Utilities
"""
import os
import configparser
import logging
from rattail.util import load_object
from asgiref.wsgi import WsgiToAsgi
log = logging.getLogger(__name__)
class TailboneWsgiToAsgi(WsgiToAsgi):
"""
Custom WSGI -> ASGI wrapper, to add routing for websockets.
"""
async def __call__(self, scope, *args, **kwargs):
protocol = scope['type']
path = scope['path']
# strip off the root path, if non-empty. needed for serving
# under /poser or anything other than true site root
root_path = scope['root_path']
if root_path and path.startswith(root_path):
path = path[len(root_path):]
if protocol == 'websocket':
websockets = self.wsgi_application.registry.get(
'tailbone_websockets', {})
if path in websockets:
await websockets[path](scope, *args, **kwargs)
try:
await super().__call__(scope, *args, **kwargs)
except ValueError as e:
# The developer may wish to improve handling of this exception.
# See https://github.com/Pylons/pyramid_cookbook/issues/225 and
# https://asgi.readthedocs.io/en/latest/specs/www.html#websocket
pass
except Exception as e:
raise e
def make_asgi_app(main_app=None):
"""
This function returns an ASGI application.
"""
path = os.environ.get('TAILBONE_ASGI_CONFIG')
if not path:
raise RuntimeError("You must define TAILBONE_ASGI_CONFIG env variable.")
# make a config parser good enough to load pyramid settings
configdir = os.path.dirname(path)
parser = configparser.ConfigParser(defaults={'__file__': path,
'here': configdir})
# read the config file
parser.read(path)
# parse the settings needed for pyramid app
settings = dict(parser.items('app:main'))
if isinstance(main_app, str):
make_wsgi_app = load_object(main_app)
elif callable(main_app):
make_wsgi_app = main_app
else:
if main_app:
log.warning("specified main app of unknown type: %s", main_app)
make_wsgi_app = load_object('tailbone.app:main')
# construct a pyramid app "per usual"
app = make_wsgi_app({}, **settings)
# then wrap it with ASGI
return TailboneWsgiToAsgi(app)
def asgi_main():
"""
This function returns an ASGI application.
"""
return make_asgi_app()

View file

@ -1,85 +1,88 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8; -*-
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2017 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
# Rattail is free software: you can redistribute it and/or modify it under the # Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free # terms of the GNU General Public License as published by the Free Software
# Software Foundation, either version 3 of the License, or (at your option) # Foundation, either version 3 of the License, or (at your option) any later
# any later version. # version.
# #
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY # Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# more details. # details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU General Public License along with
# along with Rattail. If not, see <http://www.gnu.org/licenses/>. # Rattail. If not, see <http://www.gnu.org/licenses/>.
# #
################################################################################ ################################################################################
""" """
Authentication & Authorization Authentication & Authorization
""" """
from __future__ import unicode_literals, absolute_import
import logging import logging
import re
from rattail.db import model from wuttjamaican.util import UNSPECIFIED
from rattail.db.auth import has_permission
from rattail.util import prettify, NOTSET
from zope.interface import implementer from pyramid.security import remember, forget
from pyramid.interfaces import IAuthorizationPolicy
from pyramid.security import remember, Everyone, Authenticated
from wuttaweb.auth import WuttaSecurityPolicy
from tailbone.db import Session from tailbone.db import Session
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def login_user(request, user, type_='default', timeout=NOTSET): def login_user(request, user, timeout=UNSPECIFIED):
""" """
Perform the steps necessary to login the given user. Note that this Perform the steps necessary to login the given user. Note that this
returns a ``headers`` dict which you should pass to the redirect. returns a ``headers`` dict which you should pass to the redirect.
""" """
config = request.rattail_config
app = config.get_app()
user.record_event(app.enum.USER_EVENT_LOGIN)
headers = remember(request, user.uuid) headers = remember(request, user.uuid)
if timeout is NOTSET: if timeout is UNSPECIFIED:
timeout = get_session_timeout_for_user(request.rattail_config, user, type_) or None timeout = session_timeout_for_user(config, user)
log.debug("setting session timeout for '{}' to {}".format(user.username, timeout)) log.debug("setting session timeout for '{}' to {}".format(user.username, timeout))
set_session_timeout(request, timeout) set_session_timeout(request, timeout)
return headers return headers
def get_session_timeout_for_user(config, user, type_='default'): def logout_user(request):
""" """
Must return a value to be used to set the session timeout for the given Perform the logout action for the given request. Note that this returns a
user. By default this will return ``None`` if the user has the ``headers`` dict which you should pass to the redirect.
"forever session" permission, otherwise will try to read a default
value from config:
.. code-block:: ini
[tailbone]
# set session timeout to 10 minutes:
session.timeout.default = 600
# or, set to 0 to disable:
#session.timeout.default = 0
""" """
if not has_permission(Session(), user, 'general.forever_session'): app = request.rattail_config.get_app()
timeout = config.getint('tailbone', 'session.timeout.{}'.format(type_)) user = request.user
if user:
user.record_event(app.enum.USER_EVENT_LOGOUT)
request.session.delete()
request.session.invalidate()
headers = forget(request)
return headers
# TODO: remove this hack after no longer needed
if timeout is None and type_ == 'default':
timeout = config.getint('tailbone', 'session.default_timeout')
return timeout if timeout is not None else 300 # 5 minutes def session_timeout_for_user(config, user):
"""
Returns the "max" session timeout for the user, according to roles
"""
app = config.get_app()
auth = app.get_auth_handler()
authenticated = auth.get_role_authenticated(Session())
roles = user.roles + [authenticated]
timeouts = [role.session_timeout for role in roles
if role.session_timeout is not None]
if timeouts and 0 not in timeouts:
return max(timeouts)
def set_session_timeout(request, timeout): def set_session_timeout(request, timeout):
@ -89,50 +92,42 @@ def set_session_timeout(request, timeout):
request.session['_timeout'] = timeout or None request.session['_timeout'] = timeout or None
@implementer(IAuthorizationPolicy) class TailboneSecurityPolicy(WuttaSecurityPolicy):
class TailboneAuthorizationPolicy(object):
def permits(self, context, principals, permission): def __init__(self, db_session=None, api_mode=False, **kwargs):
for userid in principals: kwargs['db_session'] = db_session or Session()
if userid not in (Everyone, Authenticated): super().__init__(**kwargs)
if context.request.user and context.request.user.uuid == userid: self.api_mode = api_mode
return context.request.has_perm(permission)
else:
assert False # should no longer happen..right?
user = Session.query(model.User).get(userid)
if user:
if has_permission(Session(), user, permission):
return True
if Everyone in principals:
return has_permission(Session(), None, permission)
return False
def principals_allowed_by_permission(self, context, permission): def load_identity(self, request):
raise NotImplementedError config = request.registry.settings.get('rattail_config')
app = config.get_app()
user = None
if self.api_mode:
def add_permission_group(config, key, label=None, overwrite=True): # determine/load user from header token if present
""" credentials = request.headers.get('Authorization')
Add a permission group to the app configuration. if credentials:
""" match = re.match(r'^Bearer (\S+)$', credentials)
def action(): if match:
perms = config.get_settings().get('tailbone_permissions', {}) token = match.group(1)
if key not in perms or overwrite: auth = app.get_auth_handler()
group = perms.setdefault(key, {'key': key}) user = auth.authenticate_user_token(self.db_session, token)
group['label'] = label or prettify(key)
config.add_settings({'tailbone_permissions': perms})
config.action(None, action)
if not user:
def add_permission(config, groupkey, key, label=None): # fetch user uuid from current session
""" uuid = self.session_helper.authenticated_userid(request)
Add a permission to the app configuration. if not uuid:
""" return
def action():
perms = config.get_settings().get('tailbone_permissions', {}) # fetch user object from db
group = perms.setdefault(groupkey, {'key': groupkey}) model = app.model
group.setdefault('label', prettify(groupkey)) user = self.db_session.get(model.User, uuid)
perm = group.setdefault('perms', {}).setdefault(key, {'key': key}) if not user:
perm['label'] = label or prettify(key) return
config.add_settings({'tailbone_permissions': perms})
config.action(None, action) # this user is responsible for data changes in current request
self.db_session.set_continuum_user(user)
return user

View file

@ -1,23 +1,23 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8; -*-
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2017 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
# Rattail is free software: you can redistribute it and/or modify it under the # Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free # terms of the GNU General Public License as published by the Free Software
# Software Foundation, either version 3 of the License, or (at your option) # Foundation, either version 3 of the License, or (at your option) any later
# any later version. # version.
# #
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY # Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# more details. # details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU General Public License along with
# along with Rattail. If not, see <http://www.gnu.org/licenses/>. # Rattail. If not, see <http://www.gnu.org/licenses/>.
# #
################################################################################ ################################################################################
""" """
@ -27,10 +27,12 @@ Note that most of the code for this module was copied from the beaker and
pyramid_beaker projects. pyramid_beaker projects.
""" """
from __future__ import unicode_literals, absolute_import
import time import time
from pkg_resources import parse_version
from rattail.util import get_pkg_version
import beaker
from beaker.session import Session from beaker.session import Session
from beaker.util import coerce_session_params from beaker.util import coerce_session_params
from pyramid.settings import asbool from pyramid.settings import asbool
@ -45,6 +47,10 @@ class TailboneSession(Session):
def load(self): def load(self):
"Loads the data from this session from persistent storage" "Loads the data from this session from persistent storage"
# are we using older version of beaker?
old_beaker = parse_version(get_pkg_version('beaker')) < parse_version('1.12')
self.namespace = self.namespace_class(self.id, self.namespace = self.namespace_class(self.id,
data_dir=self.data_dir, data_dir=self.data_dir,
digest_filenames=False, digest_filenames=False,
@ -60,8 +66,12 @@ class TailboneSession(Session):
try: try:
session_data = self.namespace['session'] session_data = self.namespace['session']
if (session_data is not None and self.encrypt_key): if old_beaker:
session_data = self._decrypt_data(session_data) if (session_data is not None and self.encrypt_key):
session_data = self._decrypt_data(session_data)
else: # beaker >= 1.12
if session_data is not None:
session_data = self._decrypt_data(session_data)
# Memcached always returns a key, its None when its not # Memcached always returns a key, its None when its not
# present # present
@ -90,6 +100,7 @@ class TailboneSession(Session):
# for this module entirely... # for this module entirely...
timeout = session_data.get('_timeout', self.timeout) timeout = session_data.get('_timeout', self.timeout)
if timeout is not None and \ if timeout is not None and \
'_accessed_time' in session_data and \
now - session_data['_accessed_time'] > timeout: now - session_data['_accessed_time'] > timeout:
timed_out = True timed_out = True
else: else:
@ -103,9 +114,6 @@ class TailboneSession(Session):
# Update the current _accessed_time # Update the current _accessed_time
session_data['_accessed_time'] = now session_data['_accessed_time'] = now
# Set the path if applicable
if '_path' in session_data:
self._path = session_data['_path']
self.update(session_data) self.update(session_data)
self.accessed_dict = session_data.copy() self.accessed_dict = session_data.copy()
finally: finally:

80
tailbone/cleanup.py Normal file
View file

@ -0,0 +1,80 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2022 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Cleanup logic
"""
from __future__ import unicode_literals, absolute_import
import os
import logging
import time
from rattail.cleanup import Cleaner
log = logging.getLogger(__name__)
class BeakerCleaner(Cleaner):
"""
Cleanup logic for old Beaker session files.
"""
def get_session_dir(self):
session_dir = self.config.get('rattail.cleanup', 'beaker.session_dir')
if session_dir and os.path.isdir(session_dir):
return session_dir
session_dir = os.path.join(self.config.appdir(), 'sessions')
if os.path.isdir(session_dir):
return session_dir
def cleanup(self, session, dry_run=False, progress=None, **kwargs):
session_dir = self.get_session_dir()
if not session_dir:
return
data_dir = os.path.join(session_dir, 'data')
lock_dir = os.path.join(session_dir, 'lock')
# looking for files older than X days
days = self.config.getint('rattail.cleanup',
'beaker.session_cutoff_days',
default=30)
cutoff = time.time() - 3600 * 24 * days
for topdir in (data_dir, lock_dir):
if not os.path.isdir(topdir):
continue
for dirpath, dirnames, filenames in os.walk(topdir):
for fname in filenames:
path = os.path.join(dirpath, fname)
ts = os.path.getmtime(path)
if ts <= cutoff:
if dry_run:
log.debug("would delete file: %s", path)
else:
os.remove(path)
log.debug("deleted file: %s", path)

View file

@ -1,38 +1,39 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8; -*-
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
# Rattail is free software: you can redistribute it and/or modify it under the # Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free # terms of the GNU General Public License as published by the Free Software
# Software Foundation, either version 3 of the License, or (at your option) # Foundation, either version 3 of the License, or (at your option) any later
# any later version. # version.
# #
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY # Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# more details. # details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU General Public License along with
# along with Rattail. If not, see <http://www.gnu.org/licenses/>. # Rattail. If not, see <http://www.gnu.org/licenses/>.
# #
################################################################################ ################################################################################
""" """
Rattail config extension for Tailbone Rattail config extension for Tailbone
""" """
from __future__ import unicode_literals, absolute_import import warnings
from wuttjamaican.conf import WuttaConfigExtension
from rattail.config import ConfigExtension as BaseExtension
from rattail.db.config import configure_session from rattail.db.config import configure_session
from tailbone.db import Session from tailbone.db import Session
class ConfigExtension(BaseExtension): class ConfigExtension(WuttaConfigExtension):
""" """
Rattail config extension for Tailbone. Does the following: Rattail config extension for Tailbone. Does the following:
@ -47,3 +48,31 @@ class ConfigExtension(BaseExtension):
def configure(self, config): def configure(self, config):
Session.configure(rattail_config=config) Session.configure(rattail_config=config)
configure_session(config, Session) configure_session(config, Session)
# provide default theme selection
config.setdefault('tailbone', 'themes.keys', 'default, butterball')
config.setdefault('tailbone', 'themes.expose_picker', 'true')
# override oruga detection
config.setdefault('wuttaweb.oruga_detector.spec', 'tailbone.util:should_use_oruga')
def csrf_token_name(config):
return config.get('tailbone', 'csrf_token_name', default='_csrf')
def csrf_header_name(config):
return config.get('tailbone', 'csrf_header_name', default='X-CSRF-TOKEN')
def global_help_url(config):
return config.get('tailbone', 'global_help_url')
def protected_usernames(config):
return config.getlist('tailbone', 'protected_usernames')
def should_expose_websockets(config):
return config.getbool('tailbone', 'expose_websockets',
usedb=False, default=False)

View file

@ -1,31 +1,29 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8; -*-
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
# Rattail is free software: you can redistribute it and/or modify it under the # Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free # terms of the GNU General Public License as published by the Free Software
# Software Foundation, either version 3 of the License, or (at your option) # Foundation, either version 3 of the License, or (at your option) any later
# any later version. # version.
# #
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY # Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# more details. # details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU General Public License along with
# along with Rattail. If not, see <http://www.gnu.org/licenses/>. # Rattail. If not, see <http://www.gnu.org/licenses/>.
# #
################################################################################ ################################################################################
""" """
Database Stuff Database sessions etc.
""" """
from __future__ import unicode_literals, absolute_import
import sqlalchemy as sa import sqlalchemy as sa
from zope.sqlalchemy import datamanager from zope.sqlalchemy import datamanager
import sqlalchemy_continuum as continuum import sqlalchemy_continuum as continuum
@ -35,23 +33,37 @@ from rattail.db import SessionBase
from rattail.db.continuum import versioning_manager from rattail.db.continuum import versioning_manager
Session = scoped_session(sessionmaker(class_=SessionBase, rattail_config=None, rattail_record_changes=False)) Session = scoped_session(sessionmaker(class_=SessionBase, rattail_config=None, expire_on_commit=False))
# not necessarily used, but here if you need it # not necessarily used, but here if you need it
TempmonSession = scoped_session(sessionmaker()) TempmonSession = scoped_session(sessionmaker())
TrainwreckSession = scoped_session(sessionmaker())
# empty dict for now, this must populated on app startup (if needed)
ExtraTrainwreckSessions = {}
class TailboneSessionDataManager(datamanager.SessionDataManager): class TailboneSessionDataManager(datamanager.SessionDataManager):
"""Integrate a top level sqlalchemy session transaction into a zope transaction """
Integrate a top level sqlalchemy session transaction into a zope
transaction
One phase variant. One phase variant.
.. note:: .. note::
This class appears to be necessary in order for the Continuum
integration to work alongside the Zope transaction integration. This class appears to be necessary in order for the
SQLAlchemy-Continuum integration to work alongside the Zope
transaction integration.
It subclasses
``zope.sqlalchemy.datamanager.SessionDataManager`` but injects
some SQLAlchemy-Continuum logic within :meth:`tpc_vote()`, and
is sort of monkey-patched into the mix.
""" """
def tpc_vote(self, trans): def tpc_vote(self, trans):
""" """
# for a one phase data manager commit last in tpc_vote # for a one phase data manager commit last in tpc_vote
if self.tx is not None: # there may have been no work to do if self.tx is not None: # there may have been no work to do
@ -63,25 +75,42 @@ class TailboneSessionDataManager(datamanager.SessionDataManager):
self._finish('committed') self._finish('committed')
def join_transaction(session, initial_state=datamanager.STATUS_ACTIVE, transaction_manager=datamanager.zope_transaction.manager, keep_session=False): def join_transaction(
"""Join a session to a transaction using the appropriate datamanager. session,
initial_state=datamanager.STATUS_ACTIVE,
transaction_manager=datamanager.zope_transaction.manager,
keep_session=False,
):
"""
Join a session to a transaction using the appropriate datamanager.
It is safe to call this multiple times, if the session is already joined It is safe to call this multiple times, if the session is already
then it just returns. joined then it just returns.
`initial_state` is either STATUS_ACTIVE, STATUS_INVALIDATED or STATUS_READONLY `initial_state` is either STATUS_ACTIVE, STATUS_INVALIDATED or
STATUS_READONLY
If using the default initial status of STATUS_ACTIVE, you must ensure that If using the default initial status of STATUS_ACTIVE, you must
mark_changed(session) is called when data is written to the database. ensure that mark_changed(session) is called when data is written
to the database.
The ZopeTransactionExtesion SessionExtension can be used to ensure that this is The ZopeTransactionExtesion SessionExtension can be used to ensure
called automatically after session write operations. that this is called automatically after session write operations.
.. note:: .. note::
This function is copied from upstream, and tweaked so that our custom
:class:`TailboneSessionDataManager` will be used. This function appears to be necessary in order for the
SQLAlchemy-Continuum integration to work alongside the Zope
transaction integration.
It overrides ``zope.sqlalchemy.datamanager.join_transaction()``
to ensure the custom :class:`TailboneSessionDataManager` is
used, and is sort of monkey-patched into the mix.
""" """
if datamanager._SESSION_STATE.get(id(session), None) is None: # the upstream internals of this function has changed a little over time.
# unfortunately for us, that means we must include each variant here.
if datamanager._SESSION_STATE.get(session, None) is None:
if session.twophase: if session.twophase:
DataManager = datamanager.TwoPhaseSessionDataManager DataManager = datamanager.TwoPhaseSessionDataManager
else: else:
@ -89,49 +118,74 @@ def join_transaction(session, initial_state=datamanager.STATUS_ACTIVE, transacti
DataManager(session, initial_state, transaction_manager, keep_session=keep_session) DataManager(session, initial_state, transaction_manager, keep_session=keep_session)
class ZopeTransactionExtension(datamanager.ZopeTransactionExtension): class ZopeTransactionEvents(datamanager.ZopeTransactionEvents):
"""Record that a flush has occurred on a session's connection. This allows """
the DataManager to rollback rather than commit on read only transactions. Record that a flush has occurred on a session's connection. This
allows the DataManager to rollback rather than commit on read only
transactions.
.. note:: .. note::
This class is copied from upstream, and tweaked so that our custom
:func:`join_transaction()` will be used. This class appears to be necessary in order for the
SQLAlchemy-Continuum integration to work alongside the Zope
transaction integration.
It subclasses
``zope.sqlalchemy.datamanager.ZopeTransactionEvents`` but
overrides various methods to ensure the custom
:func:`join_transaction()` is called, and is sort of
monkey-patched into the mix.
""" """
def after_begin(self, session, transaction, connection): def after_begin(self, session, transaction, connection):
join_transaction(session, self.initial_state, self.transaction_manager, self.keep_session) """ """
join_transaction(session, self.initial_state,
self.transaction_manager, self.keep_session)
def after_attach(self, session, instance): def after_attach(self, session, instance):
join_transaction(session, self.initial_state, self.transaction_manager, self.keep_session) """ """
join_transaction(session, self.initial_state,
self.transaction_manager, self.keep_session)
def join_transaction(self, session):
""" """
join_transaction(session, self.initial_state,
self.transaction_manager, self.keep_session)
def register(session, initial_state=datamanager.STATUS_ACTIVE, def register(
transaction_manager=datamanager.zope_transaction.manager, keep_session=False): session,
"""Register ZopeTransaction listener events on the initial_state=datamanager.STATUS_ACTIVE,
given Session or Session factory/class. transaction_manager=datamanager.zope_transaction.manager,
keep_session=False,
):
"""
Register ZopeTransaction listener events on the given Session or
Session factory/class.
This function requires at least SQLAlchemy 0.7 and makes use This function requires at least SQLAlchemy 0.7 and makes use of
of the newer sqlalchemy.event package in order to register event listeners the newer sqlalchemy.event package in order to register event
on the given Session. listeners on the given Session.
The session argument here may be a Session class or subclass, a The session argument here may be a Session class or subclass, a
sessionmaker or scoped_session instance, or a specific Session instance. sessionmaker or scoped_session instance, or a specific Session
Event listening will be specific to the scope of the type of argument instance. Event listening will be specific to the scope of the
passed, including specificity to its subclass as well as its identity. type of argument passed, including specificity to its subclass as
well as its identity.
.. note:: .. note::
This function is copied from upstream, and tweaked so that our custom
:class:`ZopeTransactionExtension` will be used. This function appears to be necessary in order for the
SQLAlchemy-Continuum integration to work alongside the Zope
transaction integration.
It overrides ``zope.sqlalchemy.datamanager.regsiter()`` to
ensure the custom :class:`ZopeTransactionEvents` is used.
""" """
from sqlalchemy import __version__
assert tuple(int(x) for x in __version__.split(".")) >= (0, 7), \
"SQLAlchemy version 0.7 or greater required to use register()"
from sqlalchemy import event from sqlalchemy import event
ext = ZopeTransactionExtension( ext = ZopeTransactionEvents(
initial_state=initial_state, initial_state=initial_state,
transaction_manager=transaction_manager, transaction_manager=transaction_manager,
keep_session=keep_session, keep_session=keep_session,
) )
@ -143,11 +197,10 @@ def register(session, initial_state=datamanager.STATUS_ACTIVE,
event.listen(session, "after_bulk_delete", ext.after_bulk_delete) event.listen(session, "after_bulk_delete", ext.after_bulk_delete)
event.listen(session, "before_commit", ext.before_commit) event.listen(session, "before_commit", ext.before_commit)
if datamanager.SA_GE_14:
event.listen(session, "do_orm_execute", ext.do_orm_execute)
# TODO: We can probably assume a new SA version since we use Continuum now.
if tuple(int(x) for x in sa.__version__.split('.')) >= (0, 7): register(Session)
register(Session) register(TempmonSession)
register(TempmonSession) register(TrainwreckSession)
else:
Session.configure(extension=ZopeTransactionExtension())
TempmonSession.configure(extension=ZopeTransactionExtension())

291
tailbone/diffs.py Normal file
View file

@ -0,0 +1,291 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tools for displaying data diffs
"""
import sqlalchemy as sa
import sqlalchemy_continuum as continuum
from pyramid.renderers import render
from webhelpers2.html import HTML
class Diff(object):
"""
Core diff class. In sore need of documentation.
You must provide the old and new data sets, and the set of
relevant fields as well, if they cannot be easily introspected.
:param old_data: Dict of "old" data values.
:param new_data: Dict of "old" data values.
:param fields: Sequence of relevant field names. Note that
both data dicts are expected to have keys which match these
field names. If you do not specify the fields then they
will (hopefully) be introspected from the old or new data
sets; however this will not work if they are both empty.
:param monospace: If true, this flag will cause the value
columns to be rendered in monospace font. This is assumed
to be helpful when comparing "raw" data values which are
shown as e.g. ``repr(val)``.
:param enums: Optional dict of enums for use when displaying field
values. If specified, keys should be field names and values
should be enum dicts.
"""
def __init__(self, old_data, new_data, columns=None, fields=None, enums=None,
render_field=None, render_value=None, nature='dirty',
monospace=False, extra_row_attrs=None):
self.old_data = old_data
self.new_data = new_data
self.columns = columns or ["field name", "old value", "new value"]
self.fields = fields or self.make_fields()
self.enums = enums or {}
self._render_field = render_field or self.render_field_default
self.render_value = render_value or self.render_value_default
self.nature = nature
self.monospace = monospace
self.extra_row_attrs = extra_row_attrs
def make_fields(self):
return sorted(set(self.old_data) | set(self.new_data), key=lambda x: x.lower())
def old_value(self, field):
return self.old_data.get(field)
def new_value(self, field):
return self.new_data.get(field)
def values_differ(self, field):
return self.new_value(field) != self.old_value(field)
def render_html(self, template='/diff.mako', **kwargs):
context = kwargs
context['diff'] = self
return HTML.literal(render(template, context))
def get_row_attrs(self, field):
"""
Returns a *rendered* set of extra attributes for the ``<tr>`` element
for the given field. May be an empty string, or a snippet of HTML
attribute syntax, e.g.:
.. code-block:: none
class="diff" foo="bar"
If you wish to supply additional attributes, please define
:attr:`extra_row_attrs`, which can be either a static dict, or a
callable returning a dict.
"""
attrs = {}
if self.values_differ(field):
attrs['class'] = 'diff'
if self.extra_row_attrs:
if callable(self.extra_row_attrs):
attrs.update(self.extra_row_attrs(field, attrs))
else:
attrs.update(self.extra_row_attrs)
return HTML.render_attrs(attrs)
def render_field(self, field):
return self._render_field(field, self)
def render_field_default(self, field, diff):
return field
def render_value_default(self, field, value):
return repr(value)
def render_old_value(self, field):
value = self.old_value(field)
return self.render_value(field, value)
def render_new_value(self, field):
value = self.new_value(field)
return self.render_value(field, value)
class VersionDiff(Diff):
"""
Special diff class, for use with version history views. Note that
while based on :class:`Diff`, this class uses a different
signature for the constructor.
:param version: Reference to a Continuum version record (object).
:param \*args: Typical usage will not require positional args
beyond the ``version`` param, in which case ``old_data`` and
``new_data`` params will be auto-determined based on the
``version``. But if you specify positional args then nothing
automatic is done, they are passed as-is to the parent
:class:`Diff` constructor.
:param \*\*kwargs: Remaining kwargs are passed as-is to the
:class:`Diff` constructor.
"""
def __init__(self, version, *args, **kwargs):
self.version = version
self.mapper = sa.inspect(continuum.parent_class(type(self.version)))
self.version_mapper = sa.inspect(type(self.version))
self.title = kwargs.pop('title', None)
if 'nature' not in kwargs:
if version.previous and version.operation_type == continuum.Operation.DELETE:
kwargs['nature'] = 'deleted'
elif version.previous:
kwargs['nature'] = 'dirty'
else:
kwargs['nature'] = 'new'
if 'fields' not in kwargs:
kwargs['fields'] = self.get_default_fields()
if not args:
old_data = {}
new_data = {}
for field in kwargs['fields']:
if version.previous:
old_data[field] = getattr(version.previous, field)
new_data[field] = getattr(version, field)
args = (old_data, new_data)
super().__init__(*args, **kwargs)
def get_default_fields(self):
fields = sorted(self.version_mapper.columns.keys())
unwanted = [
'transaction_id',
'end_transaction_id',
'operation_type',
]
return [field for field in fields
if field not in unwanted]
def render_version_value(self, field, value, version):
"""
Render the cell value text for the given version/field info.
Note that this method is used to render both sides of the diff
(before and after values).
:param field: Name of the field, as string.
:param value: Raw value for the field, as obtained from ``version``.
:param version: Reference to the Continuum version object.
:returns: Rendered text as string, or ``None``.
"""
text = HTML.tag('span', c=[repr(value)],
style='font-family: monospace;')
# assume the enum display is all we need, if enum exists for the field
if field in self.enums:
# but skip the enum display if None
display = self.enums[field].get(value)
if display is None and value is None:
return text
# otherwise show enum display to the right of raw value
display = self.enums[field].get(value, str(value))
return HTML.tag('span', c=[
text,
HTML.tag('span', c=[display],
style='margin-left: 2rem; font-style: italic; font-weight: bold;'),
])
# next we look for a relationship and may render the foreign object
for prop in self.mapper.relationships:
if prop.uselist:
continue
for col in prop.local_columns:
if col.name != field:
continue
if not hasattr(version, prop.key):
continue
if col in self.mapper.primary_key:
continue
ref = getattr(version, prop.key)
if ref:
ref = getattr(ref, 'version_parent', None)
if ref:
return HTML.tag('span', c=[
text,
HTML.tag('span', c=[str(ref)],
style='margin-left: 2rem; font-style: italic; font-weight: bold;'),
])
return text
def render_old_value(self, field):
if self.nature == 'new':
return ''
value = self.old_value(field)
return self.render_version_value(field, value, self.version.previous)
def render_new_value(self, field):
if self.nature == 'deleted':
return ''
value = self.new_value(field)
return self.render_version_value(field, value, self.version)
def as_struct(self):
values = {}
for field in self.fields:
values[field] = {'before': self.render_old_value(field),
'after': self.render_new_value(field)}
operation = None
if self.version.operation_type == continuum.Operation.INSERT:
operation = 'INSERT'
elif self.version.operation_type == continuum.Operation.UPDATE:
operation = 'UPDATE'
elif self.version.operation_type == continuum.Operation.DELETE:
operation = 'DELETE'
else:
operation = self.version.operation_type
return {
'key': id(self.version),
'model_title': self.title,
'operation': operation,
'diff_class': self.nature,
'fields': self.fields,
'values': values,
}

49
tailbone/exceptions.py Normal file
View file

@ -0,0 +1,49 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Exceptions
"""
from rattail.exceptions import RattailError
class TailboneError(RattailError):
"""
Base class for all Tailbone exceptions.
"""
class TailboneJSONFieldError(TailboneError):
"""
Error raised when JSON serialization of a form field results in an error.
This is just a simple wrapper, to make the error message more helpful for
the developer.
"""
def __init__(self, field, error):
self.field = field
self.error = error
def __str__(self):
return ("Failed to serialize field '{}' as JSON! "
"Original error was: {}".format(self.field, self.error))

View file

@ -1,38 +1,30 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8; -*-
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar # Copyright © 2010-2023 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
# Rattail is free software: you can redistribute it and/or modify it under the # Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free # terms of the GNU General Public License as published by the Free Software
# Software Foundation, either version 3 of the License, or (at your option) # Foundation, either version 3 of the License, or (at your option) any later
# any later version. # version.
# #
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY # Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# more details. # details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU General Public License along with
# along with Rattail. If not, see <http://www.gnu.org/licenses/>. # Rattail. If not, see <http://www.gnu.org/licenses/>.
# #
################################################################################ ################################################################################
""" """
Forms Forms Library
""" """
from __future__ import unicode_literals, absolute_import # nb. import widgets before types, b/c types may refer to widgets
from . import widgets
from formencode import Schema from . import types
from .core import Form, SimpleFileImport
from .core import Form, Field, FieldSet, GenericFieldSet
from .simpleform import SimpleForm, FormRenderer
from .alchemy import AlchemyForm
from .fields import AssociationProxyField
from .renderers import *
from . import renderers
from . import validators

View file

@ -1,117 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
FormAlchemy Forms
"""
from __future__ import unicode_literals, absolute_import
from rattail.core import Object
import formalchemy as fa
from pyramid.renderers import render
from webhelpers.html import HTML, tags
from tailbone.db import Session
class TemplateEngine(fa.templates.TemplateEngine):
"""
Mako template engine for FormAlchemy.
"""
def render(self, template, prefix='/forms/', suffix='.mako', **kwargs):
template = ''.join((prefix, template, suffix))
return render(template, kwargs)
class AlchemyForm(Object):
"""
Form to contain a :class:`formalchemy.FieldSet` instance.
"""
id = None
create_label = "Create"
update_label = "Save"
allow_successive_creates = False
def __init__(self, request, fieldset, session=None, csrf_field='_csrf', **kwargs):
super(AlchemyForm, self).__init__(**kwargs)
self.request = request
self.fieldset = fieldset
self.session = session
self.csrf_field = csrf_field
def _get_readonly(self):
return self.fieldset.readonly
def _set_readonly(self, val):
self.fieldset.readonly = val
readonly = property(_get_readonly, _set_readonly)
@property
def successive_create_label(self):
return "%s and continue" % self.create_label
def csrf(self, name=None):
"""
NOTE: this method was copied from `pyramid_simpleform.FormRenderer`
Returns the CSRF hidden input. Creates new CSRF token
if none has been assigned yet.
The name of the hidden field is **_csrf** by default.
"""
name = name or self.csrf_field
token = self.request.session.get_csrf_token()
if token is None:
token = self.request.session.new_csrf_token()
return tags.hidden(name, value=token)
def csrf_token(self, name=None):
"""
NOTE: this method was copied from `pyramid_simpleform.FormRenderer`
Convenience function. Returns CSRF hidden tag inside hidden DIV.
"""
return HTML.tag("div", self.csrf(name), style="display:none;")
def render(self, **kwargs):
kwargs['form'] = self
if self.readonly:
template = '/forms/form_readonly.mako'
else:
template = '/forms/form.mako'
return render(template, kwargs)
def render_fields(self):
return self.fieldset.render()
def save(self):
self.fieldset.sync()
self.session.flush()
def validate(self):
self.fieldset.rebind(data=self.request.params)
return self.fieldset.validate()

62
tailbone/forms/common.py Normal file
View file

@ -0,0 +1,62 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Common Forms
"""
from rattail.db import model
import colander
@colander.deferred
def validate_user(node, kw):
session = kw['session']
def validate(node, value):
user = session.get(model.User, value)
if not user:
raise colander.Invalid(node, "User not found")
return user.uuid
return validate
class Feedback(colander.Schema):
"""
Form schema for user feedback.
"""
email_key = colander.SchemaNode(colander.String(),
missing=colander.null)
referrer = colander.SchemaNode(colander.String())
user = colander.SchemaNode(colander.String(),
missing=colander.null,
validator=validate_user)
user_name = colander.SchemaNode(colander.String(),
missing=colander.null)
please_reply_to = colander.SchemaNode(colander.String(),
missing=colander.null)
message = colander.SchemaNode(colander.String())

File diff suppressed because it is too large Load diff

View file

@ -1,82 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2015 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Form Objects for Customer Orders
"""
from __future__ import unicode_literals
from rattail import enum
from rattail.db import model
import formencode
from formencode import validators
from tailbone.forms.validators import ValidCustomer, ValidProduct, ValidUser
class ValidCustomerInfo(validators.FormValidator):
"""
Custom validator to ensure we have either a proper customer reference, or
at least a customer name, when creating a new customer order.
"""
def validate_python(self, field_dict, state):
if not field_dict['customer'] and not field_dict['customer_name']:
raise formencode.Invalid("Customer name is required", field_dict, state)
class NewCustomerOrderItem(formencode.Schema):
"""
Form schema to which individual items on a new customer order must adhere.
"""
allow_extra_fields = True
product = ValidProduct()
product_description = validators.NotEmpty()
quantity = validators.Int()
unit_of_measure = validators.OneOf(enum.UNIT_OF_MEASURE)
discount = validators.Int()
notes = validators.String()
class NewCustomerOrder(formencode.Schema):
"""
Form schema for creating a new customer order.
"""
allow_extra_fields = True
pre_validators = [formencode.NestedVariables()]
user = formencode.Pipe(validators=[
validators.NotEmpty(),
ValidUser()])
customer = ValidCustomer()
customer_name = validators.String()
customer_phone = validators.NotEmpty()
products = formencode.ForEach(NewCustomerOrderItem())
chained_validators = [ValidCustomerInfo()]

View file

@ -1,49 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2015 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
FormAlchemy Fields
"""
from __future__ import unicode_literals, absolute_import
from formalchemy import Field
def AssociationProxyField(name, **kwargs):
"""
Returns a FormAlchemy ``Field`` class which is aware of association
proxies.
"""
class ProxyField(Field):
def sync(self):
if not self.is_readonly():
setattr(self.parent.model, self.name,
self.renderer.deserialize())
def value(model):
return getattr(model, name, None)
kwargs.setdefault('value', value)
return ProxyField(name, **kwargs)

View file

@ -0,0 +1,68 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Forms for Receiving
"""
from rattail.db import model
import colander
@colander.deferred
def valid_purchase_batch_row(node, kw):
session = kw['session']
def validate(node, value):
row = session.get(model.PurchaseBatchRow, value)
if not row:
raise colander.Invalid(node, "Batch row not found")
if row.batch.executed:
raise colander.Invalid(node, "Batch has already been executed")
return row.uuid
return validate
class ReceiveRow(colander.MappingSchema):
row = colander.SchemaNode(colander.String(),
validator=valid_purchase_batch_row)
mode = colander.SchemaNode(colander.String(),
validator=colander.OneOf([
'received',
'damaged',
'expired',
'missing',
# 'mispick',
]))
cases = colander.SchemaNode(colander.Decimal(),
missing=colander.null)
units = colander.SchemaNode(colander.Decimal(),
missing=colander.null)
expiration_date = colander.SchemaNode(colander.Date(),
missing=colander.null)
quick_receive = colander.SchemaNode(colander.Boolean())

View file

@ -1,51 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
FormAlchemy Field Renderers
"""
from __future__ import unicode_literals, absolute_import
from .core import CustomFieldRenderer, DateFieldRenderer
from .common import (StrippedTextFieldRenderer, CodeTextAreaFieldRenderer, AutocompleteFieldRenderer,
DecimalFieldRenderer, CurrencyFieldRenderer, QuantityFieldRenderer,
DateTimeFieldRenderer, DateTimePrettyFieldRenderer, TimeFieldRenderer,
EnumFieldRenderer, YesNoFieldRenderer)
from .files import FileFieldRenderer
from .people import PersonFieldRenderer, CustomerFieldRenderer
from .users import UserFieldRenderer, PermissionsFieldRenderer
from .employees import EmployeeFieldRenderer
from .stores import StoreFieldRenderer
from .vendors import VendorFieldRenderer, PurchaseFieldRenderer
from .products import (GPCFieldRenderer, ScancodeFieldRenderer,
DepartmentFieldRenderer, SubdepartmentFieldRenderer, CategoryFieldRenderer,
BrandFieldRenderer, ProductFieldRenderer,
PriceFieldRenderer, PriceWithExpirationFieldRenderer)
from .custorders import CustomerOrderFieldRenderer
from .batch import BatchIDFieldRenderer, HandheldBatchFieldRenderer

View file

@ -1,103 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Batch Field Renderers
"""
from __future__ import unicode_literals, absolute_import
import os
import stat
import random
import formalchemy as fa
from formalchemy.ext import fsblob
from formalchemy.fields import FileFieldRenderer as Base
from webhelpers.html import tags
class BatchIDFieldRenderer(fa.FieldRenderer):
"""
Renderer for batch ID fields.
"""
def render_readonly(self, **kwargs):
try:
batch_id = self.raw_value
except AttributeError:
# this can happen when creating a new batch, b/c the default value
# comes from a sequence
pass
else:
if batch_id:
return '{:08d}'.format(batch_id)
return ''
# TODO: make this inherit from `tailbone.forms.renderers.files.FileFieldRenderer`
class FileFieldRenderer(fsblob.FileFieldRenderer):
"""
Custom file field renderer for batches based on a single source data file.
In edit mode, shows a file upload field. In readonly mode, shows the
filename and its size.
"""
@classmethod
def new(cls, view):
name = 'Configured%s_%s' % (cls.__name__, str(random.random())[2:])
return type(str(name), (cls,), dict(view=view))
@property
def storage_path(self):
return self.view.upload_dir
def get_size(self):
size = super(FileFieldRenderer, self).get_size()
if size:
return size
batch = self.field.parent.model
path = os.path.join(self.view.handler.datadir(batch), self.field.value)
if os.path.isfile(path):
return os.stat(path)[stat.ST_SIZE]
return 0
def get_url(self, filename):
batch = self.field.parent.model
return self.view.request.route_url('{}.download'.format(self.view.get_route_prefix()),
uuid=batch.uuid)
def render(self, **kwargs):
return Base.render(self, **kwargs)
class HandheldBatchFieldRenderer(fa.FieldRenderer):
"""
Renderer for inventory batch's "handheld batch" field.
"""
def render_readonly(self, **kwargs):
batch = self.raw_value
if batch:
return tags.link_to(
batch.id_str,
self.request.route_url('batch.handheld.view', uuid=batch.uuid))
return ''

View file

@ -1,63 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2015 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Batch Field Renderers
"""
from __future__ import unicode_literals
import os
import stat
import random
from formalchemy.ext import fsblob
class BounceMessageFieldRenderer(fsblob.FileFieldRenderer):
"""
Custom file field renderer for email bounce messages. In readonly mode,
shows the filename and size.
"""
@classmethod
def new(cls, request, handler):
name = 'Configured%s_%s' % (cls.__name__, unicode(random.random())[2:])
return type(str(name), (cls,), dict(request=request, handler=handler))
@property
def storage_path(self):
return self.handler.root_msgdir
def get_size(self):
size = super(BounceMessageFieldRenderer, self).get_size()
if size:
return size
bounce = self.field.parent.model
path = os.path.join(self.handler.msgpath(bounce))
if os.path.isfile(path):
return os.stat(path)[stat.ST_SIZE]
return 0
def get_url(self, filename):
bounce = self.field.parent.model
return self.request.route_url('emailbounces.download', uuid=bounce.uuid)

View file

@ -1,267 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Common Field Renderers
"""
from __future__ import unicode_literals, absolute_import
import datetime
from rattail.time import localtime, make_utc
from rattail.util import pretty_quantity
import formalchemy as fa
from formalchemy import fields as fa_fields, helpers as fa_helpers
from pyramid.renderers import render
from webhelpers.html import HTML
from tailbone.util import pretty_datetime, raw_datetime
class StrippedTextFieldRenderer(fa.TextFieldRenderer):
"""
Standard text field renderer, which strips whitespace from either end of
the input value on deserialization.
"""
def deserialize(self):
value = super(StrippedTextFieldRenderer, self).deserialize()
if value is not None:
return value.strip()
class CodeTextAreaFieldRenderer(fa.TextAreaFieldRenderer):
def render_readonly(self, **kwargs):
value = self.raw_value
if not value:
return ''
return HTML.tag('pre', c=value)
def render(self, **kwargs):
kwargs.setdefault('size', (80, 8))
return super(CodeTextAreaFieldRenderer, self).render(**kwargs)
class AutocompleteFieldRenderer(fa.FieldRenderer):
"""
Custom renderer for an autocomplete field.
"""
service_route = None
width = '300px'
@property
def focus_name(self):
return self.name + '-textbox'
@property
def needs_focus(self):
return not bool(self.value or self.field_value)
@property
def field_display(self):
return self.raw_value
@property
def field_value(self):
return self.value
@property
def service_url(self):
return self.request.route_url(self.service_route)
def render(self, **kwargs):
kwargs.setdefault('field_name', self.name)
kwargs.setdefault('field_value', self.field_value)
kwargs.setdefault('field_display', self.field_display)
kwargs.setdefault('service_url', self.service_url)
kwargs.setdefault('width', self.width)
return render('/forms/field_autocomplete.mako', kwargs)
def render_readonly(self, **kwargs):
value = self.field_display
if value is None:
return u''
return unicode(value)
class DateTimeFieldRenderer(fa.DateTimeFieldRenderer):
"""
This renderer assumes the datetime field value is in UTC, and will convert
it to the local time zone before rendering it in the standard "raw" format.
"""
def render_readonly(self, **kwargs):
value = self.raw_value
if not value:
return ''
return raw_datetime(self.request.rattail_config, value)
class DateTimePrettyFieldRenderer(fa.DateTimeFieldRenderer):
"""
Custom date/time field renderer, which displays a "pretty" value in
read-only mode, leveraging config to show the correct timezone.
"""
def render_readonly(self, **kwargs):
value = self.raw_value
if not value:
return ''
return pretty_datetime(self.request.rattail_config, value)
class TimeFieldRenderer(fa.TimeFieldRenderer):
"""
Custom renderer for time fields. In edit mode, renders a simple text
input, which is expected to become a 'timepicker' widget in the UI.
However the particular magic required for that lives in 'tailbone.js'.
"""
format = '%I:%M %p'
def render(self, **kwargs):
kwargs.setdefault('class_', 'timepicker')
return fa_helpers.text_field(self.name, value=self.value, **kwargs)
def render_readonly(self, **kwargs):
return self.render_value(self.raw_value)
def render_value(self, value):
value = self.convert_value(value)
if isinstance(value, datetime.time):
return value.strftime(self.format)
return ''
def convert_value(self, value):
if isinstance(value, datetime.datetime):
if not value.tzinfo:
value = make_utc(value, tzinfo=True)
return localtime(self.request.rattail_config, value).time()
return value
def stringify_value(self, value, as_html=False):
if not as_html:
return self.render_value(value)
return super(TimeFieldRenderer, self).stringify_value(value, as_html=as_html)
def _serialized_value(self):
return self.params.getone(self.name)
def deserialize(self):
value = self._serialized_value()
if value:
try:
return datetime.datetime.strptime(value, self.format).time()
except ValueError:
pass
class EnumFieldRenderer(fa_fields.SelectFieldRenderer):
"""
Renderer for simple enumeration fields.
"""
enumeration = {}
render_key = False
def __init__(self, arg, render_key=False):
if isinstance(arg, dict):
self.enumeration = arg
self.render_key = render_key
else:
self(arg)
def __call__(self, field):
super(EnumFieldRenderer, self).__init__(field)
return self
def render_readonly(self, **kwargs):
value = self.raw_value
if value is None:
return ''
rendered = self.enumeration.get(value, unicode(value))
if self.render_key:
rendered = '{} - {}'.format(value, rendered)
return rendered
def render(self, **kwargs):
opts = [(self.enumeration[x], x) for x in self.enumeration]
if not self.field.is_required():
opts.insert(0, self.field._null_option)
return fa_fields.SelectFieldRenderer.render(self, opts, **kwargs)
class DecimalFieldRenderer(fa.FieldRenderer):
"""
Sort of generic field renderer for decimal values. You must provide the
number of places after the decimal (scale). Note that this in turn relies
on simple string formatting; the renderer does not attempt any mathematics
of its own.
"""
def __init__(self, scale):
self.scale = scale
def __call__(self, field):
super(DecimalFieldRenderer, self).__init__(field)
return self
def render_readonly(self, **kwargs):
value = self.raw_value
if value is None:
return ''
fmt = '{{0:0.{0}f}}'.format(self.scale)
return fmt.format(value)
class CurrencyFieldRenderer(fa_fields.FloatFieldRenderer):
"""
Sort of generic field renderer for currency values.
"""
def render_readonly(self, **kwargs):
value = self.raw_value
if value is None:
return ''
if value < 0:
return "(${:0,.2f})".format(0 - value)
return "${:0,.2f}".format(value)
class QuantityFieldRenderer(fa_fields.FloatFieldRenderer):
"""
Sort of generic field renderer for quantity values.
"""
def render_readonly(self, **kwargs):
return pretty_quantity(self.raw_value)
class YesNoFieldRenderer(fa.CheckBoxFieldRenderer):
def render_readonly(self, **kwargs):
value = self.raw_value
if value is None:
return u''
return u'Yes' if value else u'No'

View file

@ -1,99 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Core Field Renderers
"""
from __future__ import unicode_literals, absolute_import
import datetime
import formalchemy as fa
from formalchemy.fields import AbstractField
from pyramid.renderers import render
class CustomFieldRenderer(fa.FieldRenderer):
"""
Base class for renderers which accept customization args, and "fake out"
FormAlchemy by pretending to still be a renderer factory when in fact it's
already dealing with a renderer instance.
"""
def __init__(self, *args, **kwargs):
if len(args) == 1 and isinstance(args[0], AbstractField):
super(CustomFieldRenderer, self).__init__(args[0])
self.init(**kwargs)
else:
assert len(args) == 0
self.init(**kwargs)
def __call__(self, field):
super(CustomFieldRenderer, self).__init__(field)
return self
def init(self, **kwargs):
pass
@property
def rattail_config(self):
return self.request.rattail_config
class DateFieldRenderer(CustomFieldRenderer):
"""
Date field renderer which uses jQuery UI datepicker widget when rendering
in edit mode.
"""
date_format = None
change_year = False
def init(self, date_format=None, change_year=False):
self.date_format = date_format
self.change_year = change_year
def render_readonly(self, **kwargs):
value = self.raw_value
if value is None:
return ''
return value.strftime(self.date_format)
def render(self, **kwargs):
kwargs['name'] = self.name
kwargs['value'] = self.value
kwargs['change_year'] = self.change_year
return render('/forms/fields/date.mako', kwargs)
def deserialize(self):
value = self._serialized_value()
if not value:
return None
try:
return datetime.datetime.strptime(value, '%Y-%m-%d')
except ValueError:
raise fa.ValidationError("Date value must be in YYYY-MM-DD format")
except Exception as error:
raise fa.ValidationError(unicode(error))
def _serialized_value(self):
return self.params.getone(self.name)

View file

@ -1,42 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Customer order field renderers
"""
from __future__ import unicode_literals, absolute_import
import formalchemy as fa
from webhelpers.html import tags
class CustomerOrderFieldRenderer(fa.fields.SelectFieldRenderer):
"""
Renders a link to the customer order
"""
def render_readonly(self, **kwargs):
order = self.raw_value
if not order:
return ''
return tags.link_to(order, self.request.route_url('custorders.view', uuid=order.uuid))

View file

@ -1,47 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2017 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Employee Field Renderers
"""
from __future__ import unicode_literals, absolute_import
from webhelpers.html import tags
from tailbone.forms.renderers import AutocompleteFieldRenderer
class EmployeeFieldRenderer(AutocompleteFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Employee` instance fields.
"""
service_route = 'employees.autocomplete'
def render_readonly(self, **kwargs):
employee = self.raw_value
if not employee:
return ''
title = unicode(employee.person)
if self.request.has_perm('employees.view'):
return tags.link_to(title, self.request.route_url('employees.view', uuid=employee.uuid))
return title

View file

@ -1,98 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Batch Field Renderers
"""
from __future__ import unicode_literals, absolute_import
import os
import stat
import random
from formalchemy.ext import fsblob
from formalchemy.fields import FileFieldRenderer as Base
class FileFieldRenderer(fsblob.FileFieldRenderer):
"""
Custom file field renderer. In readonly mode, shows a filename and its
size; in edit mode, supports a single file upload.
"""
@classmethod
def new(cls, view, **kwargs):
name = b'Configured{}_{}'.format(cls.__name__, str(random.random())[2:])
return type(name, (cls,), dict(view=view, **kwargs))
@property
def request(self):
return self.view.request
@property
def storage_path(self):
return self.view.upload_dir
def get_file_path(self):
"""
Returns the absolute path to the data file.
"""
if hasattr(self, 'file_path'):
return self.file_path
return self.field.value
def get_size(self):
"""
Returns the size of the data file, in bytes.
"""
path = self.get_file_path()
if path and os.path.isfile(path):
return os.stat(path)[stat.ST_SIZE]
return 0
def get_url(self, filename):
url = self.get_download_url()
if url:
if callable(url):
return url(filename)
return url
def get_download_url(self):
if hasattr(self, 'download_url'):
return self.download_url
def render(self, **kwargs):
return Base.render(self, **kwargs)
def render_readonly(self, **kwargs):
"""
Render the filename and the binary size in a human readable with a link
to the file itself.
"""
value = self.get_file_path()
if value:
content = '{} ({})'.format(fsblob.normalized_basename(value),
self.readable_size())
return fsblob.h.content_tag('a', content,
href=self.get_url(value), **kwargs)
return ''

View file

@ -1,58 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
People Field Renderers
"""
from __future__ import unicode_literals, absolute_import
from webhelpers.html import tags
from tailbone.forms.renderers.common import AutocompleteFieldRenderer
class PersonFieldRenderer(AutocompleteFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Person` instance fields.
"""
service_route = 'people.autocomplete'
def render_readonly(self, **kwargs):
person = self.raw_value
if not person:
return ''
return tags.link_to(person, self.request.route_url('people.view', uuid=person.uuid))
class CustomerFieldRenderer(AutocompleteFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Customer` instance fields.
"""
service_route = 'customers.autocomplete'
def render_readonly(self, **kwargs):
customer = self.raw_value
if not customer:
return ''
return tags.link_to(customer, self.request.route_url('customers.view', uuid=customer.uuid))

View file

@ -1,198 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Product Field Renderers
"""
from __future__ import unicode_literals, absolute_import
from rattail.gpc import GPC
from rattail.db import model
from rattail.db.util import maxlen
from formalchemy import TextFieldRenderer
from formalchemy.fields import SelectFieldRenderer
from webhelpers.html import tags, literal
from tailbone.forms.renderers.common import AutocompleteFieldRenderer
from tailbone.util import pretty_datetime
class ProductFieldRenderer(AutocompleteFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Product` instance fields.
"""
service_route = 'products.autocomplete'
@property
def field_display(self):
product = self.raw_value
if product:
return product.full_description
return ''
def render_readonly(self, **kwargs):
product = self.raw_value
if not product:
return ''
return tags.link_to(product, self.request.route_url('products.view', uuid=product.uuid))
class ProductKeyFieldRenderer(TextFieldRenderer):
"""
Base class for product key field renderers.
"""
def render_readonly(self, **kwargs):
value = self.raw_value
if value is None:
return ''
value = self.render_value(value)
if kwargs.get('link'):
product = self.field.parent.model
value = tags.link_to(value, kwargs['link'](product))
return value
def render_value(self, value):
return unicode(value)
class GPCFieldRenderer(ProductKeyFieldRenderer):
"""
Renderer for :class:`rattail.gpc.GPC` fields.
"""
@property
def length(self):
# Hm, should maybe consider hard-coding this...?
return len(unicode(GPC(0)))
def render_value(self, gpc):
return gpc.pretty()
class ScancodeFieldRenderer(ProductKeyFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Product.scancode` field
"""
@property
def length(self):
return maxlen(model.Product.scancode)
class DepartmentFieldRenderer(SelectFieldRenderer):
"""
Shows the department number as well as the name.
"""
def render_readonly(self, **kwargs):
department = self.raw_value
if not department:
return ''
if department.number:
text = '({}) {}'.format(department.number, department.name)
else:
text = department.name
return tags.link_to(text, self.request.route_url('departments.view', uuid=department.uuid))
class SubdepartmentFieldRenderer(SelectFieldRenderer):
"""
Shows a link to the subdepartment.
"""
def render_readonly(self, **kwargs):
subdept = self.raw_value
if not subdept:
return ""
if subdept.number:
text = "({}) {}".format(subdept.number, subdept.name)
else:
text = subdept.name
return tags.link_to(text, self.request.route_url('subdepartments.view', uuid=subdept.uuid))
class CategoryFieldRenderer(SelectFieldRenderer):
"""
Shows a link to the category.
"""
def render_readonly(self, **kwargs):
category = self.raw_value
if not category:
return ""
if category.code:
text = "({}) {}".format(category.code, category.name)
else:
text = category.name
return tags.link_to(text, self.request.route_url('categories.view', uuid=category.uuid))
class BrandFieldRenderer(AutocompleteFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Brand` instance fields.
"""
service_route = 'brands.autocomplete'
class PriceFieldRenderer(TextFieldRenderer):
"""
Renderer for fields which reference a :class:`ProductPrice` instance.
"""
def render_readonly(self, **kwargs):
price = self.field.raw_value
if price:
if not price.product.not_for_sale:
if price.price is not None and price.pack_price is not None:
if price.multiple > 1:
return literal('$ %0.2f / %u&nbsp; ($ %0.2f / %u)' % (
price.price, price.multiple,
price.pack_price, price.pack_multiple))
return literal('$ %0.2f&nbsp; ($ %0.2f / %u)' % (
price.price, price.pack_price, price.pack_multiple))
if price.price is not None:
if price.multiple > 1:
return '$ %0.2f / %u' % (price.price, price.multiple)
return '$ %0.2f' % price.price
if price.pack_price is not None:
return '$ %0.2f / %u' % (price.pack_price, price.pack_multiple)
return ''
class PriceWithExpirationFieldRenderer(PriceFieldRenderer):
"""
Price field renderer which also displays the expiration date, if present.
"""
def render_readonly(self, **kwargs):
result = super(PriceWithExpirationFieldRenderer, self).render_readonly(**kwargs)
if result:
price = self.field.raw_value
if price.ends:
result = '{0}&nbsp; ({1})'.format(
result, pretty_datetime(self.request.rattail_config, price.ends))
return result

View file

@ -1,41 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2015 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Store Field Renderers
"""
from __future__ import unicode_literals
from formalchemy.fields import SelectFieldRenderer
class StoreFieldRenderer(SelectFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Store` instance fields.
"""
def render_readonly(self, **kwargs):
store = self.raw_value
if not store:
return ''
return '{0} - {1}'.format(store.id, store.name)

View file

@ -1,95 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2017 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
User Field Renderers
"""
from __future__ import unicode_literals, absolute_import
from rattail.db import model
from rattail.db.auth import has_permission, administrator_role
import formalchemy
from webhelpers.html import HTML, tags
from tailbone.db import Session
class UserFieldRenderer(formalchemy.TextFieldRenderer):
"""
Renderer for :class:`rattail:rattail.db.model.User` instance fields.
"""
def render_readonly(self, **kwargs):
user = self.raw_value
if not user:
return ''
title = user.display_name
if kwargs.get('hyperlink') and self.request.has_perm('users.view'):
return tags.link_to(title, self.request.route_url('users.view', uuid=user.uuid))
return title
def PermissionsFieldRenderer(permissions, include_guest=False, include_authenticated=False):
class PermissionsFieldRenderer(formalchemy.FieldRenderer):
def deserialize(self):
perms = []
i = len(self.name) + 1
for key in self.params:
if key.startswith(self.name):
perms.append(key[i:])
return perms
def _render(self, readonly=False, **kwargs):
principal = self.field.model
html = ''
for groupkey in sorted(permissions, key=lambda k: permissions[k]['label'].lower()):
inner = HTML.tag('p', c=permissions[groupkey]['label'])
perms = permissions[groupkey]['perms']
rendered = False
for key in sorted(perms, key=lambda p: perms[p]['label'].lower()):
checked = has_permission(Session(), principal, key,
include_guest=include_guest,
include_authenticated=include_authenticated)
if checked or not readonly:
label = perms[key]['label']
if readonly:
span = HTML.tag('span', c="[X]" if checked else "[ ]")
inner += HTML.tag('p', class_='perm', c=span + ' ' + label)
else:
inner += tags.checkbox(self.name + '-' + key,
checked=checked, label=label)
rendered = True
if rendered:
html += HTML.tag('div', class_='group', c=inner)
return html or "(none granted)"
def render(self, **kwargs):
return self._render(**kwargs)
def render_readonly(self, **kwargs):
return self._render(readonly=True, **kwargs)
return PermissionsFieldRenderer

View file

@ -1,57 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Vendor Field Renderers
"""
from __future__ import unicode_literals, absolute_import
from formalchemy.fields import SelectFieldRenderer
from webhelpers.html import tags
from tailbone.forms.renderers.common import AutocompleteFieldRenderer
class VendorFieldRenderer(AutocompleteFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Vendor` instance fields.
"""
service_route = 'vendors.autocomplete'
def render_readonly(self, **kwargs):
vendor = self.raw_value
if not vendor:
return ''
return tags.link_to(vendor, self.request.route_url('vendors.view', uuid=vendor.uuid))
class PurchaseFieldRenderer(SelectFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Purchase` relation fields.
"""
def render_readonly(self, **kwargs):
purchase = self.raw_value
if not purchase:
return ''
return tags.link_to(purchase, self.request.route_url('purchases.view', uuid=purchase.uuid))

View file

@ -1,83 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Simple Forms
"""
from __future__ import unicode_literals, absolute_import
from rattail.util import prettify
import pyramid_simpleform
from pyramid_simpleform import renderers
from webhelpers.html import tags
from webhelpers.html import HTML
from tailbone.forms import Form
class SimpleForm(Form):
"""
Customized simple form.
"""
def __init__(self, request, schema, obj=None, **kwargs):
super(SimpleForm, self).__init__(request, **kwargs)
self._form = pyramid_simpleform.Form(request, schema=schema, obj=obj)
def __getattr__(self, attr):
return getattr(self._form, attr)
def render(self, **kwargs):
kwargs['form'] = FormRenderer(self)
return super(SimpleForm, self).render(**kwargs)
def validate(self):
return self._form.validate()
class FormRenderer(renderers.FormRenderer):
"""
Customized form renderer. Provides some extra methods for convenience.
"""
def __getattr__(self, attr):
return getattr(self.form, attr)
def field_div(self, name, field, label=None):
errors = self.errors_for(name)
if errors:
errors = [HTML.tag('div', class_='field-error', c=x) for x in errors]
errors = tags.literal('').join(errors)
label = HTML.tag('label', for_=name, c=label or prettify(name))
inner = HTML.tag('div', class_='field', c=field)
outer_class = 'field-wrapper'
if errors:
outer_class += ' error'
outer = HTML.tag('div', class_=outer_class, c=(errors or '') + label + inner)
return outer
def referrer_field(self):
return self.hidden('referrer', value=self.form.request.get_referrer())

265
tailbone/forms/types.py Normal file
View file

@ -0,0 +1,265 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Form Schema Types
"""
import re
import datetime
import json
from rattail.db import model
from rattail.gpc import GPC
import colander
from tailbone.db import Session
from tailbone.forms import widgets
class JQueryTime(colander.Time):
"""
Custom type for jQuery widget Time data.
"""
def deserialize(self, node, cstruct):
if not cstruct:
return colander.null
formats = [
'%I:%M %p',
'%I:%M%p',
'%I %p',
'%I%p',
]
for fmt in formats:
try:
return datetime.datetime.strptime(cstruct, fmt).time()
except ValueError:
pass
# re-try first format, for "better" error message
return datetime.datetime.strptime(cstruct, formats[0]).time()
class DateTimeBoolean(colander.Boolean):
"""
Schema type which presents the user with a "boolean" whereas the underlying
node is really a datetime (assumed to be "naive" UTC, and allow nulls).
"""
def deserialize(self, node, cstruct):
value = super(DateTimeBoolean, self).deserialize(node, cstruct)
if value: # else return None
return datetime.datetime.utcnow()
class FalafelDateTime(colander.DateTime):
"""
Custom schema node type for rattail UTC datetimes
"""
widget_maker = widgets.FalafelDateTimeWidget
def __init__(self, *args, **kwargs):
request = kwargs.pop('request')
super().__init__(*args, **kwargs)
self.request = request
def serialize(self, node, appstruct):
if not appstruct:
return {}
# cant use isinstance; dt subs date
if type(appstruct) is datetime.date:
appstruct = datetime.datetime.combine(appstruct, datetime.time())
if not isinstance(appstruct, datetime.datetime):
raise colander.Invalid(node, f'"{appstruct}" is not a datetime object')
if appstruct.tzinfo is None:
appstruct = appstruct.replace(tzinfo=self.default_tzinfo)
app = self.request.rattail_config.get_app()
dt = app.localtime(appstruct, from_utc=True)
return {
'date': str(dt.date()),
'time': str(dt.time()),
}
def deserialize(self, node, cstruct):
if not cstruct:
return colander.null
if not cstruct['date'] and not cstruct['time']:
return colander.null
try:
date = datetime.datetime.strptime(cstruct['date'], '%Y-%m-%d').date()
except:
node.raise_invalid("Missing or invalid date")
try:
time = datetime.datetime.strptime(cstruct['time'], '%H:%M:%S').time()
except:
node.raise_invalid("Missing or invalid time")
result = datetime.datetime.combine(date, time)
app = self.request.rattail_config.get_app()
result = app.localtime(result)
result = app.make_utc(result)
return result
class FalafelTime(colander.Time):
"""
Custom schema node type for simple time fields
"""
widget_maker = widgets.FalafelTimeWidget
def __init__(self, *args, **kwargs):
request = kwargs.pop('request')
super().__init__(*args, **kwargs)
self.request = request
class GPCType(colander.SchemaType):
"""
Schema type for product GPC data.
"""
def serialize(self, node, appstruct):
if appstruct is colander.null:
return colander.null
return str(appstruct)
def deserialize(self, node, cstruct):
if not cstruct:
return None
digits = re.sub(r'\D', '', cstruct)
if not digits:
return None
try:
return GPC(digits)
except Exception as err:
raise colander.Invalid(node, str(err))
class ProductQuantity(colander.MappingSchema):
"""
Combo schema type for product cases and units; useful for inventory,
ordering, receiving etc. Meant to be used with the ``CasesUnitsWidget``.
"""
cases = colander.SchemaNode(colander.Decimal(), missing=colander.null)
units = colander.SchemaNode(colander.Decimal(), missing=colander.null)
class ModelType(colander.SchemaType):
"""
Custom schema type for scalar ORM relationship fields.
"""
model_class = None
session = None
def __init__(self, model_class=None, session=None):
if model_class:
self.model_class = model_class
if session:
self.session = session
else:
self.session = self.make_session()
def make_session(self):
return Session()
@property
def model_title(self):
self.model_class.get_model_title()
def serialize(self, node, appstruct):
if appstruct is colander.null:
return colander.null
return str(appstruct)
def deserialize(self, node, cstruct):
if not cstruct:
return None
obj = self.session.get(self.model_class, cstruct)
if not obj:
raise colander.Invalid(node, "{} not found".format(self.model_title))
return obj
# TODO: deprecate / remove this
ObjectType = ModelType
class StoreType(ModelType):
"""
Custom schema type for store field.
"""
model_class = model.Store
class CustomerType(ModelType):
"""
Custom schema type for customer field.
"""
model_class = model.Customer
class DepartmentType(ModelType):
"""
Custom schema type for department field.
"""
model_class = model.Department
class EmployeeType(ModelType):
"""
Custom schema type for employee field.
"""
model_class = model.Employee
class VendorType(ModelType):
"""
Custom schema type for vendor relationship field.
"""
model_class = model.Vendor
class ProductType(ModelType):
"""
Custom schema type for product relationship field.
"""
model_class = model.Product
class UserType(ModelType):
"""
Custom schema type for user field.
"""
model_class = model.User

View file

@ -1,153 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Custom Form Validators
"""
from __future__ import unicode_literals, absolute_import
import re
from rattail.db import model
from rattail.db.util import validate_email_address, validate_phone_number
from rattail.gpc import GPC
import formencode as fe
import formalchemy as fa
from tailbone.db import Session
class ValidGPC(fe.validators.FancyValidator):
"""
Validator for fields which should contain GPC value.
"""
def _to_python(self, value, state):
if value is not None:
digits = re.sub(r'\D', '', value)
if digits:
try:
return GPC(digits)
except ValueError as error:
raise fe.Invalid("Invalid UPC: {}".format(error), value, state)
def _from_python(self, upc, state):
if upc is None:
return ''
return upc.pretty()
class ModelValidator(fe.validators.FancyValidator):
"""
Generic validator for data model reference fields.
"""
model_class = None
@property
def model_name(self):
self.model_class.__name__
def _to_python(self, value, state):
if value:
obj = Session.query(self.model_class).get(value)
if obj:
return obj
raise fe.Invalid("{} not found".format(self.model_name), value, state)
def _from_python(self, value, state):
obj = value
if not obj:
return ''
return obj.uuid
def validate_python(self, value, state):
obj = value
if obj is not None and not isinstance(obj, self.model_class):
raise fe.Invalid("Value must be a valid {} object".format(self.model_name), value, state)
class ValidStore(ModelValidator):
"""
Validator for store field.
"""
model_class = model.Store
class ValidCustomer(ModelValidator):
"""
Validator for customer field.
"""
model_class = model.Customer
class ValidDepartment(ModelValidator):
"""
Validator for department field.
"""
model_class = model.Department
class ValidEmployee(ModelValidator):
"""
Validator for employee field.
"""
model_class = model.Employee
class ValidProduct(ModelValidator):
"""
Validator for product field.
"""
model_class = model.Product
class ValidUser(ModelValidator):
"""
Validator for user field.
"""
model_class = model.User
def valid_email_address(value, field=None):
"""
FormAlchemy-compatible validation function, which leverages FormEncode
under the hood.
"""
if value:
try:
return validate_email_address(value, error=True)
except Exception as error:
raise fa.ValidationError(unicode(error))
def valid_phone_number(value, field=None):
"""
FormAlchemy-compatible validation function, which leverages FormEncode
under the hood.
"""
if value:
try:
return validate_phone_number(value, error=True)
except Exception as error:
raise fa.ValidationError(unicode(error))

659
tailbone/forms/widgets.py Normal file
View file

@ -0,0 +1,659 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Form Widgets
"""
import json
import datetime
import decimal
import re
import colander
from deform import widget as dfwidget
from webhelpers2.html import tags, HTML
from tailbone.db import Session
class ReadonlyWidget(dfwidget.HiddenWidget):
readonly = True
def serialize(self, field, cstruct, **kw):
""" """
if cstruct in (colander.null, None):
cstruct = ''
# TODO: is this hacky?
text = kw.get('text')
if not text:
text = field.parent.tailbone_form.render_field_value(field.name)
return HTML.tag('span', text) + tags.hidden(field.name, value=cstruct, id=field.oid)
class NumberInputWidget(dfwidget.TextInputWidget):
template = 'numberinput'
autocomplete = 'off'
class NumericInputWidget(NumberInputWidget):
"""
This widget uses a ``<numeric-input>`` component, which will
leverage the ``numeric.js`` functions to ensure user doesn't enter
any non-numeric values. Note that this still uses a normal "text"
input on the HTML side, as opposed to a "number" input, since the
latter is a bit ugly IMHO.
"""
template = 'numericinput'
allow_enter = True
class PercentInputWidget(dfwidget.TextInputWidget):
"""
Custom text input widget, used for "percent" type fields. This widget
assumes that the underlying storage for the value is a "traditional"
percent value, e.g. ``0.36135`` - but the UI should represent this as a
"human-friendly" value, e.g. ``36.135 %``.
"""
template = 'percentinput'
autocomplete = 'off'
def serialize(self, field, cstruct, **kw):
""" """
if cstruct not in (colander.null, None):
# convert "traditional" value to "human-friendly"
value = decimal.Decimal(cstruct) * 100
value = value.quantize(decimal.Decimal('0.001'))
cstruct = str(value)
return super().serialize(field, cstruct, **kw)
def deserialize(self, field, pstruct):
""" """
pstruct = super().deserialize(field, pstruct)
if pstruct is colander.null:
return colander.null
# convert "human-friendly" value to "traditional"
try:
value = decimal.Decimal(pstruct)
except decimal.InvalidOperation:
raise colander.Invalid(field.schema, "Invalid decimal string: {}".format(pstruct))
value = value.quantize(decimal.Decimal('0.00001'))
value /= 100
return str(value)
class CasesUnitsWidget(dfwidget.Widget):
"""
Widget for collecting case and/or unit quantities. Most useful when you
need to ensure user provides cases *or* units but not both.
"""
template = 'cases_units'
amount_required = False
one_amount_only = False
def serialize(self, field, cstruct, **kw):
""" """
if cstruct in (colander.null, None):
cstruct = ''
readonly = kw.get('readonly', self.readonly)
kw['cases'] = cstruct['cases'] or ''
kw['units'] = cstruct['units'] or ''
template = readonly and self.readonly_template or self.template
values = self.get_template_values(field, cstruct, kw)
return field.renderer(template, **values)
def deserialize(self, field, pstruct):
""" """
from tailbone.forms.types import ProductQuantity
if pstruct is colander.null:
return colander.null
schema = ProductQuantity()
try:
validated = schema.deserialize(pstruct)
except colander.Invalid as exc:
raise colander.Invalid(field.schema, "Invalid pstruct: %s" % exc)
if self.amount_required and not (validated['cases'] or validated['units']):
raise colander.Invalid(field.schema, "Must provide case or unit amount",
value=validated)
if self.amount_required and self.one_amount_only and validated['cases'] and validated['units']:
raise colander.Invalid(field.schema, "Must provide case *or* unit amount, "
"but *not* both", value=validated)
return validated
class DynamicCheckboxWidget(dfwidget.CheckboxWidget):
"""
This checkbox widget can be "dynamic" in the sense that form logic can
control its value and state.
"""
template = 'checkbox_dynamic'
# TODO: deprecate / remove this
class PlainSelectWidget(dfwidget.SelectWidget):
template = 'select_plain'
class CustomSelectWidget(dfwidget.SelectWidget):
"""
This widget is mostly for convenience. You can set extra kwargs for the
:meth:`serialize()` method, e.g.::
widget.set_template_values(foo='bar')
"""
def set_template_values(self, **kw):
if not hasattr(self, 'extra_template_values'):
self.extra_template_values = {}
self.extra_template_values.update(kw)
def get_template_values(self, field, cstruct, kw):
values = super().get_template_values(field, cstruct, kw)
if hasattr(self, 'extra_template_values'):
values.update(self.extra_template_values)
return values
class DynamicSelectWidget(CustomSelectWidget):
"""
This is a "normal" select widget, but instead of (or in addition to) its
values being set when constructed, they must be assigned dynamically in
real-time, e.g. based on other user selections.
Really all this widget "does" is render some Vue.js-compatible HTML, but
the page which contains the widget is ultimately responsible for wiring up
the logic for things to work right.
"""
template = 'select_dynamic'
class JQuerySelectWidget(dfwidget.SelectWidget):
template = 'select_jquery'
class PlainDateWidget(dfwidget.DateInputWidget):
template = 'date_plain'
class JQueryDateWidget(dfwidget.DateInputWidget):
"""
Uses the jQuery datepicker UI widget, instead of whatever it is deform uses
by default.
"""
template = 'date_jquery'
type_name = 'text'
requirements = None
default_options = (
('changeMonth', True),
('changeYear', True),
('dateFormat', 'yy-mm-dd'),
)
def serialize(self, field, cstruct, **kw):
""" """
if cstruct in (colander.null, None):
cstruct = ''
readonly = kw.get('readonly', self.readonly)
template = readonly and self.readonly_template or self.template
options = dict(
kw.get('options') or self.options or self.default_options
)
options.update(kw.get('extra_options', {}))
kw.setdefault('options_json', json.dumps(options))
kw.setdefault('selected_callback', None)
values = self.get_template_values(field, cstruct, kw)
return field.renderer(template, **values)
class JQueryTimeWidget(dfwidget.TimeInputWidget):
"""
Uses the jQuery datepicker UI widget, instead of whatever it is deform uses
by default.
"""
template = 'time_jquery'
type_name = 'text'
requirements = None
default_options = (
('showPeriod', True),
)
class FalafelDateTimeWidget(dfwidget.DateTimeInputWidget):
"""
Custom widget for rattail UTC datetimes
"""
template = 'datetime_falafel'
new_pattern = re.compile(r'^\d\d?:\d\d:\d\d [AP]M$')
def serialize(self, field, cstruct, **kw):
""" """
readonly = kw.get('readonly', self.readonly)
values = self.get_template_values(field, cstruct, kw)
template = self.readonly_template if readonly else self.template
return field.renderer(template, **values)
def deserialize(self, field, pstruct):
""" """
if pstruct == '':
return colander.null
# nb. we now allow '4:20:00 PM' on the widget side, but the
# true node needs it to be '16:20:00' instead
if self.new_pattern.match(pstruct['time']):
time = datetime.datetime.strptime(pstruct['time'], '%I:%M:%S %p')
pstruct['time'] = time.strftime('%H:%M:%S')
return pstruct
class FalafelTimeWidget(dfwidget.TimeInputWidget):
"""
Custom widget for simple time fields
"""
template = 'time_falafel'
def deserialize(self, field, pstruct):
""" """
if pstruct == '':
return colander.null
return pstruct
class JQueryAutocompleteWidget(dfwidget.AutocompleteInputWidget):
"""
Uses the jQuery autocomplete plugin, instead of whatever it is deform uses
by default.
"""
template = 'autocomplete_jquery'
requirements = None
field_display = ""
assigned_label = None
service_url = None
cleared_callback = None
selected_callback = None
input_callback = None
new_label_callback = None
ref = None
default_options = (
('autoFocus', True),
)
options = None
def serialize(self, field, cstruct, **kw):
""" """
if 'delay' in kw or getattr(self, 'delay', None):
raise ValueError(
'AutocompleteWidget does not support *delay* parameter '
'any longer.'
)
if cstruct in (colander.null, None):
cstruct = ''
self.values = self.values or []
readonly = kw.get('readonly', self.readonly)
options = dict(
kw.get('options') or self.options or self.default_options
)
options['source'] = self.service_url
kw['options'] = json.dumps(options)
kw['field_display'] = self.field_display
kw['cleared_callback'] = self.cleared_callback
kw['assigned_label'] = self.assigned_label
kw['input_callback'] = self.input_callback
kw['new_label_callback'] = self.new_label_callback
kw['ref'] = self.ref
kw.setdefault('selected_callback', self.selected_callback)
tmpl_values = self.get_template_values(field, cstruct, kw)
template = readonly and self.readonly_template or self.template
return field.renderer(template, **tmpl_values)
class FileUploadWidget(dfwidget.FileUploadWidget):
"""
Widget to handle file upload. Must override to add ``use_oruga``
to field template context.
"""
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
def get_template_values(self, field, cstruct, kw):
values = super().get_template_values(field, cstruct, kw)
if self.request:
values['use_oruga'] = self.request.use_oruga
return values
class MultiFileUploadWidget(dfwidget.FileUploadWidget):
"""
Widget to handle multiple (arbitrary number) of file uploads.
"""
template = 'multi_file_upload'
requirements = ()
def serialize(self, field, cstruct, **kw):
""" """
if cstruct in (colander.null, None):
cstruct = []
if cstruct:
for fileinfo in cstruct:
uid = fileinfo['uid']
if uid not in self.tmpstore:
self.tmpstore[uid] = fileinfo
readonly = kw.get("readonly", self.readonly)
template = readonly and self.readonly_template or self.template
values = self.get_template_values(field, cstruct, kw)
return field.renderer(template, **values)
def deserialize(self, field, pstruct):
""" """
if pstruct is colander.null:
return colander.null
# TODO: why is this a thing? pstruct == [b'']
if len(pstruct) == 1 and pstruct[0] == b'':
return colander.null
files_data = []
for upload in pstruct:
data = self.deserialize_upload(upload)
if data:
files_data.append(data)
if not files_data:
return colander.null
return files_data
def deserialize_upload(self, upload):
""" """
# nb. this logic was copied from parent class and adapted
# to allow for multiple files. needs some more love.
uid = None # TODO?
if hasattr(upload, "file"):
# the upload control had a file selected
data = dfwidget.filedict()
data["fp"] = upload.file
filename = upload.filename
# sanitize IE whole-path filenames
filename = filename[filename.rfind("\\") + 1 :].strip()
data["filename"] = filename
data["mimetype"] = upload.type
data["size"] = upload.length
if uid is None:
# no previous file exists
while 1:
uid = self.random_id()
if self.tmpstore.get(uid) is None:
data["uid"] = uid
self.tmpstore[uid] = data
preview_url = self.tmpstore.preview_url(uid)
self.tmpstore[uid]["preview_url"] = preview_url
break
else:
# a previous file exists
data["uid"] = uid
self.tmpstore[uid] = data
preview_url = self.tmpstore.preview_url(uid)
self.tmpstore[uid]["preview_url"] = preview_url
else:
# the upload control had no file selected
if uid is None:
# no previous file exists
return colander.null
else:
# a previous file should exist
data = self.tmpstore.get(uid)
# but if it doesn't, don't blow up
if data is None:
return colander.null
return data
def make_customer_widget(request, **kwargs):
"""
Make a customer widget; will be either autocomplete or dropdown
depending on config.
"""
# use autocomplete widget by default
factory = CustomerAutocompleteWidget
# caller may request dropdown widget
if kwargs.pop('dropdown', False):
factory = CustomerDropdownWidget
else: # or, config may say to use dropdown
if request.rattail_config.getbool(
'rattail', 'customers.choice_uses_dropdown',
default=False):
factory = CustomerDropdownWidget
# instantiate whichever
return factory(request, **kwargs)
class CustomerAutocompleteWidget(JQueryAutocompleteWidget):
"""
Autocomplete widget for a
:class:`~rattail:rattail.db.model.customers.Customer` reference
field.
"""
def __init__(self, request, *args, **kwargs):
super().__init__(*args, **kwargs)
self.request = request
app = self.request.rattail_config.get_app()
model = app.model
# must figure out URL providing autocomplete service
if 'service_url' not in kwargs:
# caller can just pass 'url' instead of 'service_url'
if 'url' in kwargs:
self.service_url = kwargs['url']
else: # use default url
self.service_url = self.request.route_url('customers.autocomplete')
# TODO
if 'input_callback' not in kwargs:
if 'input_handler' in kwargs:
self.input_callback = input_handler
def serialize(self, field, cstruct, **kw):
""" """
# fetch customer to provide button label, if we have a value
if cstruct:
app = self.request.rattail_config.get_app()
model = app.model
customer = Session.get(model.Customer, cstruct)
if customer:
self.field_display = str(customer)
return super().serialize(
field, cstruct, **kw)
class CustomerDropdownWidget(dfwidget.SelectWidget):
"""
Dropdown widget for a
:class:`~rattail:rattail.db.model.customers.Customer` reference
field.
"""
def __init__(self, request, *args, **kwargs):
super().__init__(*args, **kwargs)
self.request = request
app = self.request.rattail_config.get_app()
# must figure out dropdown values, if they weren't given
if 'values' not in kwargs:
# use what caller gave us, if they did
if 'customers' in kwargs:
customers = kwargs['customers']
if callable(customers):
customers = customers()
else: # default customer list
customers = app.get_clientele_handler()\
.get_all_customers(Session())
# convert customer list to option values
self.values = [(c.uuid, c.name)
for c in customers]
class DepartmentWidget(dfwidget.SelectWidget):
"""
Custom select widget for a Department reference field.
Constructor accepts the normal ``values`` kwarg but if not
provided then the widget will fetch department list from Rattail
DB.
Constructor also accepts ``required`` kwarg, which defaults to
true unless specified.
"""
def __init__(self, request, **kwargs):
if 'values' not in kwargs:
app = request.rattail_config.get_app()
model = app.model
departments = Session.query(model.Department)\
.order_by(model.Department.number)
values = [(dept.uuid, str(dept))
for dept in departments]
if not kwargs.pop('required', True):
values.insert(0, ('', "(none)"))
kwargs['values'] = values
super().__init__(**kwargs)
def make_vendor_widget(request, **kwargs):
"""
Make a vendor widget; will be either autocomplete or dropdown
depending on config.
"""
# use autocomplete widget by default
factory = VendorAutocompleteWidget
# caller may request dropdown widget
if kwargs.pop('dropdown', False):
factory = VendorDropdownWidget
else: # or, config may say to use dropdown
app = request.rattail_config.get_app()
vendor_handler = app.get_vendor_handler()
if vendor_handler.choice_uses_dropdown():
factory = VendorDropdownWidget
# instantiate whichever
return factory(request, **kwargs)
class VendorAutocompleteWidget(JQueryAutocompleteWidget):
"""
Autocomplete widget for a Vendor reference field.
"""
def __init__(self, request, *args, **kwargs):
super().__init__(*args, **kwargs)
self.request = request
app = self.request.rattail_config.get_app()
model = app.model
# must figure out URL providing autocomplete service
if 'service_url' not in kwargs:
# caller can just pass 'url' instead of 'service_url'
if 'url' in kwargs:
self.service_url = kwargs['url']
else: # use default url
self.service_url = self.request.route_url('vendors.autocomplete')
# # TODO
# if 'input_callback' not in kwargs:
# if 'input_handler' in kwargs:
# self.input_callback = input_handler
def serialize(self, field, cstruct, **kw):
""" """
# fetch vendor to provide button label, if we have a value
if cstruct:
app = self.request.rattail_config.get_app()
model = app.model
vendor = Session.get(model.Vendor, cstruct)
if vendor:
self.field_display = str(vendor)
return super().serialize(
field, cstruct, **kw)
class VendorDropdownWidget(dfwidget.SelectWidget):
"""
Dropdown widget for a Vendor reference field.
"""
def __init__(self, request, *args, **kwargs):
super().__init__(*args, **kwargs)
self.request = request
# must figure out dropdown values, if they weren't given
if 'values' not in kwargs:
# use what caller gave us, if they did
if 'vendors' in kwargs:
vendors = kwargs['vendors']
if callable(vendors):
vendors = vendors()
else: # default vendor list
app = self.request.rattail_config.get_app()
model = app.model
vendors = Session.query(model.Vendor)\
.order_by(model.Vendor.name)\
.all()
# convert vendor list to option values
self.values = [(c.uuid, c.name)
for c in vendors]

Some files were not shown because too many files have changed in this diff Show more