From bd0f8da16b4f0d70d32a378b90a5909c46307f26 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Fri, 19 Aug 2022 06:42:40 -0700 Subject: [PATCH] working on ezcater ezcater other. dajusments. migration for square2 --- .projectile | 4 + resources/public/img/ezcater.png | Bin 0 -> 17726 bytes resources/public/img/koala.png | Bin 0 -> 1936 bytes resources/public/img/square.png | Bin 0 -> 4328 bytes scratch-sessions/dynamo-replica.clj | 63 ++ .../working-on-ezcater-square-v2.clj | 235 ++++++++ src/clj/auto_ap/datomic/migrate/sales.clj | 40 +- src/clj/auto_ap/datomic/sales_orders.clj | 6 + src/clj/auto_ap/ezcater/core.clj | 233 ++++---- src/clj/auto_ap/graphql.clj | 3 + src/clj/auto_ap/graphql/sales_orders.clj | 15 +- src/clj/auto_ap/jobs/square2.clj | 9 + src/clj/auto_ap/routes/ezcater.clj | 1 - src/clj/auto_ap/square/core.clj | 4 +- src/clj/auto_ap/square/core2.clj | 541 ++++++++++++++++++ src/clj/user.clj | 4 +- src/cljs/auto_ap/views/pages/pos/form.cljs | 15 +- .../auto_ap/views/pages/pos/sales_orders.cljs | 5 +- .../auto_ap/views/pages/pos/side_bar.cljs | 43 +- src/cljs/auto_ap/views/pages/pos/table.cljs | 41 +- test/clj/auto_ap/ezcater_test.clj | 51 +- 21 files changed, 1158 insertions(+), 155 deletions(-) create mode 100644 .projectile create mode 100644 resources/public/img/ezcater.png create mode 100644 resources/public/img/koala.png create mode 100644 resources/public/img/square.png create mode 100644 scratch-sessions/dynamo-replica.clj create mode 100644 scratch-sessions/working-on-ezcater-square-v2.clj create mode 100644 src/clj/auto_ap/jobs/square2.clj create mode 100644 src/clj/auto_ap/square/core2.clj diff --git a/.projectile b/.projectile new file mode 100644 index 00000000..51430329 --- /dev/null +++ b/.projectile @@ -0,0 +1,4 @@ +-/target +-/node_modules +-/data +-/datomic-init diff --git a/resources/public/img/ezcater.png b/resources/public/img/ezcater.png new file mode 100644 index 0000000000000000000000000000000000000000..090064413eb659ee457e5f2164e12699e539449a GIT binary patch literal 17726 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 literal 0 HcmV?d00001 diff --git a/resources/public/img/koala.png b/resources/public/img/koala.png new file mode 100644 index 0000000000000000000000000000000000000000..d39976a88cc999146a3d1a0ca6bd760a64b34990 GIT binary patch literal 1936 zcmb7_XEYm(8pl&LLZebIY7?(flu{K`k)(+&G&bR?Hr5-vHdPU%s?@3|72K%T9wBPa z8ogGvHr1g;NKv)db zqn*y5hVHIU-9KG)xBgoo901@DG(y6yLT8xstHEWGU^W++1(I*!)YCkNuC@7A#U*Lr zyRU6aWQR&VMzwG*5pYiOEFK160Y3t;{@$AZ|2(f{K_a2Z_40`?RfgVpjay4i3B$rl z7xJ=sR5b(JpY;`b8MI2TYe!srxts0&yq9|<9_TF-Z2S$;qC5FeuxffxEh|pY>ypy8 z&G8O1JW6*0KkHjxu$PU~xy4E7@++H{LHX5FkL@PrYrR@u9g}aTf-aRyW(mcqNtKY3 z-)uPHHHEv%s1YUWmp@p}c2S*f>^6yw@nz}>6dJWtBqjn5nua!dPkEroaZAMy0_Okzw?g!6% ziMtnfYK1!LaC}}JjPY}8B7WMvFkbmx>2_KF_R8@wB4T~OyR?v!eHHThqIE+$zK76~ zH@-x5yQc@$mhTFPOe>J?ah=OhP7Co~cs#x&WwaTW&&^h6 z-i+^sfQt3SuOaF8+`>e8OIAV3{)=GGI_~Y6ptlwxzL}?gbz4MR7ISM59vUoo=g#F$ zE27@j5k1VWKFetXm+0Mc@VX$U~KJXbK#1I6g3Y^*~SHYBW3@E0X?scoF`q zJb9B=mn|VZ87mv5ijhHbrh@(Z=XLrK@BnoW z$e~)DM#@s;ueixpac6oJVdtPMU7NDOc=MiNIXd=!vn!5%1!A~j6@()t@-bMAYTK?8 zA@z8;@Qt-h2ws*Lo$$a*_h8=?7>xr`IPY3gdZSr-LRS!6!T)U=$rYS+Qc9D<#m|Dp zconEqIZ6y~Stm%Kuuml@Rhv5+OTJ?ppRc6Cge~t1LgUwpZblr-QMT*`99x(l|1>18 zeLIr;$TXUCj&Ip*V2B;)n?yRxkD4^#VCN zzqeyh)&QysR!D=7-c&p@U0;9#4Olv#*j&p?5%` za&ZGUy%SP_NV~|C=2rEw3z=?ArsoQ z0?(3~yj9cGl*?H7Wjt(~+qZc2zD8>L4e7pxO^Z~3dNkMAxqfU>TwXWpo2iF1&>&EC z_(j9g&}ZazYhCZ5wI#RJau`HpO13DbSompZb+yY}hw<7$}mRp=KEVjPx!Hp{r&A&H2?oW{Okrfw=)QnC6F>1i`5Efc?W9lpK-d zYEDjs^O3{y4#N87<;EZ7!}rW%z-O4^iP?B0iCE@|sXl`tEH}$A*B{Ldgqa$11A9mr z_IpbpN))JYA55BZ^rNV)1XN9`p%2V|T9=#-ga(mXPxBQ<_VYGGNvEgK`}?`8)XOL4 zguRn4eYZC7o-BA_m3Lw}A;heXi9QPdVM@wU_-+0C&pv#=udU6tZxX$XlsuHC8t6>1 N03#F{S&48V{2OcCT(bZG literal 0 HcmV?d00001 diff --git a/resources/public/img/square.png b/resources/public/img/square.png new file mode 100644 index 0000000000000000000000000000000000000000..4d33275f2ef7ecd03c63ab776eb180c4e685adef GIT binary patch literal 4328 zcmdT{`9GB1-#;@98boB7ETfH@m26{%Y}sdqMA53UwyB8dZf2~dqA1Be*>Ve6Dj`gi zHlbA#qg1k!aL1T=&bXiF4|sn1zP|IC*Id{6d_L#Be9oEcysiW{rh`0s6&e5_@8r0} z9RMV}MFJTKcxfqG_Y7Vn1FRX=090O<73@dBF(Sm>!3LDKYYxFmt{dIM&fz$nL}S7f zp5j8Lx)RNpcrzx6#vsy|1S-?h)00GV!PDsa=8lGj{xD9axxxt6(voD(AW>ZK6j)3r z(ikK&2*NfrglG)18PgILETzb%u0(S>1Se4GL@IMB$i&l_BzR9{8d}o-jp)H41fejA z6jvgZv82I;1gDt0KxC+jK!Yhdf#w212vi1)i%HFwaG++fNQeMgm^e#MoTak?l?g3C zK(RtGxGTxdPCVuRzG5*;HpSrP5SamQ^fyCN2FZdU&O#y@9Zz)=>$KR4w}8!Y!Qo&- z7!-;p$qb5wQt2=Y*&!E{4O>Anb6Mx;h=T zG-7DzNr0;(cK5GesDTM7AoWrw;dEFjwl6jdr!2`J(jXda8XUsL!Uf$5!O51c24>E1 zqSy%hg|p!HfN`3uiHYOC6mSYuE9Qc&hGiCXGR2K(vURB&ODr@d-qei@xt6vBj6;zm zb2l-dIouU+hd?Du+e7RHN_7)=3Cf@`ja2W>!QIIZb!Tn^LWDry<99*7k;LMp3hH*i zs0@92;Zc+U5`E7_@*i^@+X=HXb3eabE?GGqCqMRu7}oc3+ls53OMiT?zPYBuG|+2k z=GP}V^;Iu=hhH&dIUxs=@^bRCEbabYU%2{I(XF+E7_IkG1Iuiv*{^4V%RhgWR^k!% zyB=cQUw0t=q_uI3n)k&!DH@SZ+RFTQ@y_yzCk_Sk5t#IB3AvQc8_NFrl@_DabW zNjuUFTGci5KT4_b)}LT}Rq8pRNvj-CqBXdtr2jTaU+`>JTDk6B*xUt+xvaT}#wgJQ zIr;8rpXZ^hovebn>gwCF{D`uWW7hX=2XZD(?ueWz%!=7DpTydm!F}j4^nRmO;_eL@ zDjn(f@=E(;pR$Ki--V6eyv56^q3Mm7v(C0P9%|>-JkI%C%OZB|>x($1v~VV`#WGW4 zX1b<)4{uWY(5IJ!`KK2#_>A<=Ax(;*zV<^7k4CS1{h0NA6@;k~3APk_}b+kJ~3C)dB!EZa}r&@=QEoc{71jZEV*ezXcqfV<;@m$(xfoFB3sLo zk@!N^F9;1!lnHIWPLuO>uzfBC4Rjem?l>lLp*qTJQB zmkqXTf1|)5i&DKs^|t#Cpwo%f!Kmck#G1Ifm9d@LO)Qan{FR;Ur@#)3 z%^}%eF`!64_M-_35n2)Ru}B(WR2B2_WXuH0N2(TOsT&!K`KJ08u_6ug?Za_-UO=19 z11#>;brnfd&%fW7>O9y~i1xNV?uwsTj+YsK;C`nvx_{iWTNJ6q>}gjd-U z&#E+ef(&z##m;cGqYX#MoH)I0l&KpQzqhAlgW)&7YL7Wu%pS>$117p5-B&Vuj%aY4 zf_58cp+9^=ac@Rv#(Q7wSOdmfPjpt$(gWo=QsY>sJD-p~N+J=~JJ=h^;fvO^?F&S4 z5_fNUlX9B?+@A79$!Z4?oQSN~c1`-)Je$3uQ`>SnLIK_^?{=T5ChsrdV#gi@_2t}n z&q&Y<{M$l}!unlVAP`j%l#pNZek16V>2Jh2giGrotu#0a zX_Wb0C~y>*Q1=$gfDB;%HDQ64SdZYMIP7#&Kxsm8R6&_`ZBz9+ETDqjQ)n<~0e%9- zK@2zxI)P5N;aoTqSV?di`VbY0U={FE#?#z(rVD^9;cUq?H7CKY#&ukXF(h=R2SI;o5K^Mkil%aFo&TuGm`}(TN5Y zAnbAESK;7F@Ef=V?R~(WKLCCr`}wy-!frn_r~}2 zxao@4h44`~&?#r7l6B#2sE|FH(8|VwRzlOaN2Us?Pwgv93m0m%*+x{+DWItRpKR3s zn>$MZtW)Qa;9ndagD42uJeP`*(=muzs2vZAK~OHAwCw8LzlkINx#QFbU8?i(;_jL@ zUoa73S@T{Rj#Haad&1fbFq~-_wa{*bHmQnq*}B{PWPgbb%pdXP)S(;lyyxHJkG6%^vVV>P#u@HWVoNQ}2W0NQc^hkD`Ukt&906-U;FN!GbS~f{qiV$bLbRT5R+D zV$l$n-wn3u)Yun#Y&3i}meU`gc(L31$VhasT~5pL0}BTfd4w>p^xs?Ry=;9H`u;L{h5MlRk#p|;l%bW+K8=4u zOM*sMm!FYFo=&$YNTvL|*w8VpVDlz&|D-aS=C#G^t+d25-xc0Ro4)VLUVL?AP05R0 zx$c>*x+xd5e$B{ZT)gevh_+adJ6yy*)mTxcKBRGYwjwcBwdm46*>-oTlQOopIJF%-doTa& zQB0Hc&V9YM`K+MzmlN+cq~^0jzT7A1R@F6EHrD@=FsZ8j!`GPZaWSn*d17HO+ttN- zMvEOQ)4fL4Z>RCxuFsYwtUenz<3R8WIC{}Wpfs&|zpCYqS(*68oxPz_xFw&gSl=yjkupKnwD%X)A%N6;2|N^C$0hzt*T# zd1_izywBUX*E-s5KK_hN5{XgtsX%>&?L2?g7AxuFdV52SN~KpjdTczvRd%F~8L#S# zkv-mF^5r0Rt&jU>i^#0DeuOOUzCowf%JKkg_6AKkpZ&JWS^&a3LM!)mLSB^QnwMQT zeK~^u*oaDqc47ho3=3+rs^nsjj`r#2r}-Ma&Nh0y(tVoOcinoT|C6^_;>pJEDhmpl xa?AX%sCQD2-rhnnjO7SdYf}BxQTd#m07TtQLdA!C=@LKCoa~re%58k2{|5+TI4uAG literal 0 HcmV?d00001 diff --git a/scratch-sessions/dynamo-replica.clj b/scratch-sessions/dynamo-replica.clj new file mode 100644 index 00000000..68e8bc2f --- /dev/null +++ b/scratch-sessions/dynamo-replica.clj @@ -0,0 +1,63 @@ +;; This buffer is for Clojure experiments and evaluation. + +;; Press C-j to evaluate the last expression. + +;; You can also press C-u C-j to evaluate the expression and pretty-print its result. + +(ns dynamo-replica + (:require [amazonica.aws.dynamodbv2 :as ddb] + [datomic.api :as d] + [auto-ap.datomic :refer [conn]])) + +(ddb/delete-table :table-name "sales-replica") + +(ddb/create-table + :table-name "sales-replica" + :key-schema [{:attribute-name "id" :key-type "HASH"} + {:attribute-name "date" :key-type "RANGE"}] + :attribute-definitions + [{:attribute-name "id" :attribute-type "S"} + {:attribute-name "date" :attribute-type "S"} + {:attribute-name "client" :attribute-type "N"}] + :global-secondary-indexes + [{:index-name "client_date" + :key-schema [{:attribute-name "client" :key-type "HASH"} + {:attribute-name "date" :key-type "RANGE"}] + :projection {:projection-type "ALL"} + :provisioned-throughput + {:read-capacity-units 500 + :write-capacity-units 500}}] + :provisioned-throughput + {:read-capacity-units 500 + :write-capacity-units 500}) + +(user/init-repl) + +(doseq [client ["NGAK"] + batch (->> (d/q '[:find [?s ...] + :in $ ?c + :where + [?s :sales-order/client ?c]] + (d/db conn) + [:client/code client]) + + (partition-all 25) + (map (fn [batch] + (for [o (d/pull-many (d/db conn) '[* {:sales-order/charges [*] :sales-order/line-items [*]}] batch)] + {:id (str (:db/id o)) + :client (:db/id (:sales-order/client o)) + :date (str (:sales-order/date o)) + :total (:sales-order/total o) + #_#_:line-items (into-array (for [i (:sales-order/line-items o)] + (java.util.HashMap. {:total (:order-line-item/total i)})))}))))] + (println "Adding 25 ... " (first batch)) + (ddb/batch-write-item :request-items + {"sales-replica" + (for [item batch] + {:put-request {:item item}})}) + (Thread/sleep 50) + #_(ddb/put-item :table-name "sales-replica" + :item item)) + +(doc d/pull-many) + diff --git a/scratch-sessions/working-on-ezcater-square-v2.clj b/scratch-sessions/working-on-ezcater-square-v2.clj new file mode 100644 index 00000000..8a67fda9 --- /dev/null +++ b/scratch-sessions/working-on-ezcater-square-v2.clj @@ -0,0 +1,235 @@ +;; This buffer is for Clojure experiments and evaluation. + +;; Press C-j to evaluate the last expression. + +;; You can also press C-u C-j to evaluate the expression and pretty-print its result. +(ns working-on-ezcater-square-v2 + (:require [auto-ap.square.core :as sc] + [clj-time.core :as time] + [auto-ap.ezcater.core :as ez]) + ) + +(auto-ap.routes.queries/put-query + "ec3bae96-95cd-40aa-b0b1-342ab58efed2" + " [:find ?d4 ?type ?p2 (sum ?total) (sum ?tip) + :with ?charge + :in $ + :where + [?charge :charge/date ?date] + [(ground (clj-time.coerce/to-date (clj-time.core/minus (auto-ap.time/local-now) (clj-time.core/days 50)))) ?min-d] + [(>= ?date ?min-d)] + [?charge :charge/client ?c] + [?c :client/code \"NGOP2\"] + [?charge :charge/type-name ?type] + [?charge :charge/total ?total] + [?charge :charge/tip ?tip] + (or + + (and [_ :expected-deposit/charges ?charge ] + [(ground :settlement) ?ccp] + [(ground :settlement) ?p]) + (and + (not [_ :expected-deposit/charges ?charge]) + [(get-else $ ?charge :charge/processor :na) ?ccp] + [(get-else $ ?ccp :db/ident :na) ?p] + )) + [(name ?p) ?p2] + [(clj-time.coerce/to-date-time ?date) ?d2] + [(auto-ap.time/localize ?d2) ?d3] + [(auto-ap.time/unparse-local ?d3 auto-ap.time/normal-date) ?d4]] +", "") + +(user/init-repl) + +(in-ns 'auto-ap.square.core) +(require '[auto-ap.square.core :as sc]) + +(require '[auto-ap.ezcater.core :as ez]) + +(def square-client (first (sc/get-square-clients "NGOP2"))) + + +(def square-location (first (for [client (sc/get-square-clients "NGOP2") + square-location (:client/square-locations client) + :when (:square-location/client-location square-location)] + square-location))) + + +(sc/upsert square-client square-location (time/plus (time/now) (time/days -45)) (time/now)) + +(sc/upsert-settlements square-client square-location) + +(sc/get-order square-client square-location "EYIVOtQj8FgdpgNlpUQP9S51uNbZY" ) +(sc/get-payment square-client "vhnZ67vi7BIO6mZ6R609Vsih7nDZY") +k +(sc/get-order square-client square-location "ggjaHEikb81AKjrJC7H4RRqjDXZZY") + +(sc/get-payment square-client "zhEgwmQL4cI0P00NGBAzqX5gnX6YY") + +(sc/get-order square-client square-location "CdzNsHRryhGNQ9fh4tdYcEBwA4PZY") + +(sc/get-order square-client square-location "hipr01buRJcFg2NFeFTjuJ4eV") + + +(user/start-db) + +(def my-search (doall (search (first (get-square-clients "NGOP2")) + {:db/id 17592186106155, + :square-location/name "Oyster Point", + :square-location/square-id "L3GMNBFARX9GG", + :square-location/client-location "OP"} + #clj-time/date-time "2022-06-30T00:00:00-08:00" + #clj-time/date-time "2022-07-01T00:00:00-08:00" + + ))) + +(def my-search (doall (search (first (get-square-clients "NGOP2")) + {:db/id 17592186106155, + :square-location/name "Oyster Point", + :square-location/square-id "L3GMNBFARX9GG", + :square-location/client-location "OP"} + #clj-time/date-time "2022-07-13T00:00:00-08:00" + #clj-time/date-time "2022-07-14T00:00:00-08:00" + + ))) + +(set/difference + + + (filter + + (comp (set/difference + (set (map :id + (let [search-results my-search + koala-production-ids (->> search-results + (filter #(= "koala-production" (:name (:source %)))) + (map :id) + (into #{}))] + (let [search-results my-search] + (->> search-results + (filter (fn [order] + ;; sometimes orders stay open in square. At least one payment + ;; is needed to import, in order to avoid importing orders in-progress. + (and (or (> (count (:tenders order)) 0) + (seq (:returns order))) + (or (= #{} (set (map #(:status (:card_details %)) (:tenders order)))) + (not= #{} (set/difference + (set (map #(:status (:card_details %)) (:tenders order))) + #{"FAILED" "VOIDED"})))))) + (filter (fn [order] + (not= "Koala" (:name (:source order))))) + (filter (fn has-linked-koala-production? [order] + ;; if a POS order is linked (via note) to a koala-production order, we want + ;; to keep the koala-production order, because it has taxes correct + (not (and (:line_items order) ;; returns do not have line items, so they should be allowed + (->> (:line_items order) + (map :note) + (every? koala-production-ids)))))))))) + ) + (set (map :id (->> my-search + (filter (fn [order] + (and (or (> (count (:tenders order)) 0) + (seq (:returns order))) + (or (= #{} (set (map #(:status (:card_details %)) (:tenders order)))) + (not= #{} (set/difference + (set (map #(:status (:card_details %)) (:tenders order))) + #{"FAILED" "VOIDED"}))) + (not= ["CUSTOM_AMOUNT"] + (mapv :item_type (:line_items order ))) + )))) + ))) + :id + ) + my-search)) + + +(doseq [p (->> + (d/query {:query {:find ['?e] + :in ['$] + :where ['(or [?e :sales-order/client [:client/code "NGOP2"]] + [?e :sales-refund/client [:client/code "NGOP2"]] + [?e :expected-deposit/client [:client/code "NGOP2"]])]} + :args [(d/db conn)]}) + (map first) + (map (fn [x] [:db/retractEntity x])) + (partition-all 100))] + @(d/transact conn p)) + + +(def weird-order + {:total_service_charge_money {:amount 0, :currency "USD"}, + :closed_at "2022-07-14T02:50:43Z", + :total_discount_money {:amount 0, :currency "USD"}, + :total_money {:amount 120533, :currency "USD"}, + :total_tip_money {:amount 0, :currency "USD"}, + :taxes + [{:uid "64a788f3-13c6-45a8-9333-c7e4ed4b45f2", + :catalog_object_id "4XTWECYYOZWTGRJ475C3QOQL", + :catalog_version 1655153159309, + :name "9.875%", + :percentage "9.875", + :type "ADDITIVE", + :applied_money {:amount 10833, :currency "USD"}, + :scope "LINE_ITEM"}], + :net_amounts + {:total_money {:amount 120533, :currency "USD"}, + :tax_money {:amount 10833, :currency "USD"}, + :discount_money {:amount 0, :currency "USD"}, + :tip_money {:amount 0, :currency "USD"}, + :service_charge_money {:amount 0, :currency "USD"}}, + :total_tax_money {:amount 10833, :currency "USD"}, + :location_id "L3GMNBFARX9GG", + :tenders + [{:id "pQgHRVNc3WzBDZVGpORONh5YvaB", + :location_id "L3GMNBFARX9GG", + :transaction_id "XBo0x9zb7rkpWLAJ79cgihweV", + :created_at "2022-07-14T02:50:42Z", + :note "ezcater 7\\13", + :amount_money {:amount 120533, :currency "USD"}, + :processing_fee_money {:amount 0, :currency "USD"}, + :type "OTHER"}], + :state "COMPLETED", + :ticket_name "Ezcater 7-13", + :updated_at "2022-07-14T02:50:43Z", + :net_amount_due_money {:amount 0, :currency "USD"}, + :return_amounts + {:total_money {:amount 0, :currency "USD"}, + :tax_money {:amount 0, :currency "USD"}, + :discount_money {:amount 0, :currency "USD"}, + :tip_money {:amount 0, :currency "USD"}, + :service_charge_money {:amount 0, :currency "USD"}}, + :line_items + [{:total_discount_money {:amount 0, :currency "USD"}, + :total_money {:amount 120533, :currency "USD"}, + :total_tax_money {:amount 10833, :currency "USD"}, + :uid "6a293751-027e-45a0-9f5d-e9f4e7cb62fd", + :variation_total_price_money {:amount 109700, :currency "USD"}, + :item_type "CUSTOM_AMOUNT", + :gross_sales_money {:amount 109700, :currency "USD"}, + :applied_taxes + [{:uid "64a788f3-13c6-45a8-9333-c7e4ed4b45f2", + :tax_uid "64a788f3-13c6-45a8-9333-c7e4ed4b45f2", + :applied_money {:amount 10833, :currency "USD"}}], + :base_price_money {:amount 109700, :currency "USD"}, + :quantity "1"}], + :id "XBo0x9zb7rkpWLAJ79cgihweV", + :created_at "2022-07-14T02:50:43Z"}) + +(order->sales-order "a" "b" weird-order) + + +(doseq [n ["185431988" "186982950" "186344682" "185281242" "184313395" "185477889" "185043055" "184552573" "185042091" "182674641" "184083782" "184200746" "181197904"]] + (clojure.pprint/pprint + @(d/transact auto-ap.datomic/conn [(-> {"id" "bf3dcf5c-a68f-42d9-9084-049133e03d3d", "parent_type" "Caterer", "parent_id" "ef505599-947f-42da-a36c-4b569e33a747", "entity_type" "Order", "entity_id" "178289110", "key" "accepted", "occurred_at" "2022-07-21T19:21:07.549Z"} + (assoc "entity_id" n) + (ez/lookup-order) + (ez/order->sales-order) + (update :sales-order/date coerce/to-date) + (update-in [:sales-order/charges 0 :charge/date] coerce/to-date))]))) + +;; +(clojure.pprint/pprint [(-> {"id" "bf3dcf5c-a68f-42d9-9084-049133e03d3d", "parent_type" "Caterer", "parent_id" "91541331-d7ae-4634-9e8b-ccbbcfb2ce70", "entity_type" "Order", "entity_id" "178289110", "key" "accepted", "occurred_at" "2022-07-21T19:21:07.549Z"} + (lookup-order) + #_(order->sales-order) + #_(update :sales-order/date coerce/to-date) + #_(update-in [:sales-order/charges 0 :charge/date] coerce/to-date))]) diff --git a/src/clj/auto_ap/datomic/migrate/sales.clj b/src/clj/auto_ap/datomic/migrate/sales.clj index 13386624..81cb9a98 100644 --- a/src/clj/auto_ap/datomic/migrate/sales.clj +++ b/src/clj/auto_ap/datomic/migrate/sales.clj @@ -292,5 +292,43 @@ :db/index true}]] :requires [:add-orders]} :add-ezcater-vendor {:txes [[{:db/ident :vendor/ccp-ezcater - :vendor/name "EZCater CCP"}]]}}) + :vendor/name "EZCater CCP"}]]} + :add-charge-client {:txes [[{:db/ident :charge/client + :db/doc "The client for the sale" + :db/valueType :db.type/ref + :db/cardinality :db.cardinality/one} + + {:db/ident :charge/location + :db/doc "The location of the sale" + :db/valueType :db.type/string + :db/cardinality :db.cardinality/one}]]} + :add-charge-date {:txes [[{:db/ident :charge/date + :db/doc "The date for the payment" + :db/valueType :db.type/instant + :db/cardinality :db.cardinality/one}]]} + :add-ezcater-processor {:txes [[{:db/ident :ccp-processor/ezcater + :db/doc "ezcater processor"}]]} + :add-koala-processor {:txes [[{:db/ident :ccp-processor/koala + :db/doc "koala processor"}]]} + :add-square-processor {:txes [[{:db/ident :ccp-processor/square + :db/doc "square processor"}]]} + :add-reference-link3 {:txes [[{:db/ident :sales-order/reference-link + :db/cardinality :db.cardinality/one + :db/valueType :db.type/string + :db/doc "A link someone can go to to look at the order"} + {:db/ident :charge/reference-link + :db/cardinality :db.cardinality/one + :db/valueType :db.type/string + :db/doc "A link someone can go to to look at the charge"}]]} + :add-source-field2 {:txes [[{:db/ident :sales-order/source + :db/cardinality :db.cardinality/one + :db/valueType :db.type/string + :db/doc "Where the POS says the order is from"} + {:db/ident :charge/note + :db/cardinality :db.cardinality/one + :db/valueType :db.type/string + :db/doc "A custom note"}]]} + :add-feature-flags {:txes [[{:db/ident :client/feature-flags + :db/valueType :db.type/string + :db/cardinality :db.cardinality/many}]]}}) diff --git a/src/clj/auto_ap/datomic/sales_orders.clj b/src/clj/auto_ap/datomic/sales_orders.clj index ecfde7bc..3d624dbe 100644 --- a/src/clj/auto_ap/datomic/sales_orders.clj +++ b/src/clj/auto_ap/datomic/sales_orders.clj @@ -29,6 +29,7 @@ (:sort args) (add-sorter-fields {"client" ['[?e :sales-order/client ?c] '[?c :client/name ?sort-client]] "location" ['[?e :sales-order/location ?sort-location]] + "source" ['[?e :sales-order/source ?sort-source]] "date" ['[?e :sales-order/date ?sort-date]] "total" ['[?e :sales-order/total ?sort-total]] "tax" ['[?e :sales-order/tax ?sort-tax]] @@ -68,6 +69,11 @@ '[?chg :charge/processor ?processor]]} :args [(keyword "ccp-processor" (name (:processor args)))]}) + (:type-name args) + (merge-query {:query {:in ['?type-name] + :where ['[?e :sales-order/charges ?chg] + '[?chg :charge/type-name ?type-name]]} + :args [(:type-name args)]}) (:total-gte args) (merge-query {:query {:in ['?total-gte] diff --git a/src/clj/auto_ap/ezcater/core.clj b/src/clj/auto_ap/ezcater/core.clj index 1be6f0c9..865a4505 100644 --- a/src/clj/auto_ap/ezcater/core.clj +++ b/src/clj/auto_ap/ezcater/core.clj @@ -9,7 +9,8 @@ [clojure.tools.logging :as log] [clj-time.core :as time] [clojure.set :as set] - [auto-ap.time :as atime])) + [auto-ap.time :as atime] + [cemerick.url :as url])) (defn query [{:ezcater-integration/keys [api-key]} q] (-> (client/post "https://api.ezcater.com/graphql/" @@ -118,118 +119,156 @@ [:ezcater-caterer/uuid caterer-uuid])) +(defn round-carry-cents [f] + (with-precision 2 (double (.setScale (bigdec f) 2 java.math.RoundingMode/HALF_UP)))) (defn commision [order] - (let [commision% (if (= "MARKETPLACE" (:orderSourceType order)) - 0.15 - 0.07)] - (* commision% - 0.01 - (+ - (-> order :totals :subTotal :subunits ) - (reduce + - 0 - (map :subunits (:feesAndDiscounts (:catererCart order)))))))) + + (let [commision% (cond + (= "CLUB_SODA" (:orderSourceType order)) + 0.25M + + (= "MARKETPLACE" (:orderSourceType order)) + 0.15M + :else + 0.07M)] + (round-carry-cents + (* commision% + 0.01M + (+ + (-> order :totals :subTotal :subunits ) + (reduce + + 0 + (map (comp :subunits :cost) (:feesAndDiscounts (:catererCart order))))))))) (defn ccp-fee [order] - (* 0.000275 - (+ - (-> order :totals :subTotal :subunits ) - (-> order :totals :salesTax :subunits ) - (reduce + - 0 - (map :subunits (:feesAndDiscounts (:catererCart order))))))) + (round-carry-cents + (* 0.000275M + (+ + (-> order :totals :subTotal :subunits ) + (-> order :totals :salesTax :subunits ) + (reduce + + 0 + (map (comp :subunits :cost) (:feesAndDiscounts (:catererCart order)))))))) (defn order->sales-order [{{:keys [timestamp]} :event {:keys [orderItems]} :catererCart :keys [client-code client-location uuid] :as order}] - #:sales-order - {:date (atime/localize (coerce/to-date-time timestamp)) - :external-id (str "ezcater/order/" client-code "-" client-location "-" uuid) - :client [:client/code client-code] - :location client-location - :line-items (->> orderItems - (map-indexed (fn [i li] - #:order-line-item - {:external-id (str "ezcater/order/" client-code "-" client-location "-" uuid "-" i) - :item-name (:name li) - :category "External Catering" - :total (* 0.01 (:subunits (:totalInSubunits li)))}))) + (let [adjustment (round-carry-cents (- (+ (-> order :totals :subTotal :subunits (* 0.01)) + (-> order :totals :salesTax :subunits (* 0.01))) + (-> order :catererCart :totals :catererTotalDue ) + (commision order) + (ccp-fee order))) + service-charge (+ (commision order) (ccp-fee order)) + tax (-> order :totals :salesTax :subunits (* 0.01)) + tip (-> order :totals :tip :subunits (* 0.01))] + #:sales-order + {:date (atime/localize (coerce/to-date-time timestamp)) + :external-id (str "ezcater/order/" client-code "-" client-location "-" uuid) + :client [:client/code client-code] + :location client-location + :reference-link (str (url/url "https://ezmanage.ezcater.com/orders/" uuid )) + :line-items [#:order-line-item + {:external-id (str "ezcater/order/" client-code "-" client-location "-" uuid "-" 0) + :item-name "EZCater Catering" + :category "EZCater Catering" + :discount adjustment + :tax tax + :total (+ (-> order :totals :subTotal :subunits (* 0.01)) + tax + tip)}] + :charges [#:charge + {:type-name "CARD" + :date (atime/localize (coerce/to-date-time timestamp)) + :client [:client/code client-code] + :location client-location + :external-id (str "ezcater/charge/" uuid) + :processor :ccp-processor/ezcater + :total (+ (-> order :totals :subTotal :subunits (* 0.01)) + tax + tip) + :tip tip}] - :total (-> order :catererCart :totals :catererTotalDue ) - :discount (- (+ (-> order :totals :subTotal :subunits (* 0.01)) - (-> order :totals :salesTax :subunits (* 0.01))) - (-> order :catererCart :totals :catererTotalDue ) - (commision order) - (ccp-fee order)) - :service-charge (+ (commision order) (ccp-fee order)) - :tax (-> order :totals :salesTax :subunits (* 0.01)) - :tip (-> order :totals :tip :subunits (* 0.01)) - :vendor :vendor/ccp-ezcater}) + :total (+ (-> order :totals :subTotal :subunits (* 0.01)) + tax + tip) + :discount adjustment + :service-charge service-charge + :tax tax + :tip tip + :returns 0.0 + :vendor :vendor/ccp-ezcater})) (defn lookup-order [json] (let [caterer (get-caterer (get json "parent_id")) integration (:ezcater-integration/_caterers caterer) client (-> caterer :ezcater-location/_caterer first :client/_ezcater-locations :client/code) location (-> caterer :ezcater-location/_caterer first :ezcater-location/location)] - (-> (query - integration - {:venia/queries [[:order {:id (get json "entity_id")} - [:uuid - :orderSourceType - [:caterer - [:name - :uuid - [:address [:street]]]] - [:event - [:timestamp - :catererHandoffFoodTime - :orderType]] - [:catererCart [[:orderItems - [:name - :quantity - :posItemId - [:totalInSubunits - [:currency - :subunits]]]] - [:totals - [:catererTotalDue]] - [:feesAndDiscounts - {:type 'DELIVERY_FEE} - [[:cost - [:currency - :subunits]]]]]] - [:totals [[:customerTotalDue - [ - :currency - :subunits - ]] - [:pointOfSaleIntegrationFee - [ - :currency - :subunits - ]] - [:tip - [:currency - :subunits]] - [:salesTax - [ - :currency - :subunits - ]] - [:salesTaxRemittance - [:currency - :subunits - ]] - [:subTotal - [:currency - :subunits]]]]]]]}) - (:order) - (assoc :client-code client - :client-location location)))) + (doto + (-> (query + integration + {:venia/queries [[:order {:id (get json "entity_id")} + [:uuid + :orderSourceType + [:caterer + [:name + :uuid + [:address [:street]]]] + [:event + [:timestamp + :catererHandoffFoodTime + :orderType]] + [:catererCart [[:orderItems + [:name + :quantity + :posItemId + [:totalInSubunits + [:currency + :subunits]]]] + [:totals + [:catererTotalDue]] + [:feesAndDiscounts + {:type 'DELIVERY_FEE} + [[:cost + [:currency + :subunits]]]]]] + [:totals [[:customerTotalDue + [ + :currency + :subunits + ]] + [:pointOfSaleIntegrationFee + [ + :currency + :subunits + ]] + [:tip + [:currency + :subunits]] + [:salesTax + [ + :currency + :subunits + ]] + [:salesTaxRemittance + [:currency + :subunits + ]] + [:subTotal + [:currency + :subunits]]]]]]]}) + (:order) + (assoc :client-code client + :client-location location)) + log/info))) (defn import-order [json] ;; {"id" "bf3dcf5c-a68f-42d9-9084-049133e03d3d", "parent_type" "Caterer", "parent_id" "91541331-d7ae-4634-9e8b-ccbbcfb2ce70", "entity_type" "Order", "entity_id" "9ab05fee-a9c5-483b-a7f2-14debde4b7a8", "key" "accepted", "occurred_at" "2022-07-21T19:21:07.549Z"} + (clojure.pprint/pprint [(-> json + (lookup-order) + (order->sales-order) + (update :sales-order/date coerce/to-date) + (update-in [:sales-order/charges 0 :charge/date] coerce/to-date))]) @(d/transact conn [(-> json (lookup-order) (order->sales-order) - (update :sales-order/date coerce/to-date))]) - ) + (update :sales-order/date coerce/to-date) + (update-in [:sales-order/charges 0 :charge/date] coerce/to-date))])) diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index 2d64ce86..1d3f2fa9 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -506,6 +506,9 @@ :enums { :processor {:values [{:enum-value :na} {:enum-value :doordash} + {:enum-value :koala} + {:enum-value :ezcater} + {:enum-value :square} {:enum-value :uber_eats} {:enum-value :grubhub}]} :integration_state {:values [{:enum-value :failed} diff --git a/src/clj/auto_ap/graphql/sales_orders.clj b/src/clj/auto_ap/graphql/sales_orders.clj index da260b08..4beb07d0 100644 --- a/src/clj/auto_ap/graphql/sales_orders.clj +++ b/src/clj/auto_ap/graphql/sales_orders.clj @@ -31,6 +31,8 @@ {:fields {:id {:type :id} :location {:type 'String} :external_id {:type 'String} + :reference_link {:type 'String} + :source {:type 'String} :total {:type :money} :tip {:type :money} :tax {:type :money} @@ -50,11 +52,13 @@ :category {:type 'String} :discount {:type :money}}} :charge - {:fields {:id {:type :id} - :processor {:type :processor} - :type_name {:type 'String} - :total {:type :money} - :tip {:type :money} + {:fields {:id {:type :id} + :processor {:type :processor} + :reference_link {:type 'String} + :note {:type 'String} + :type_name {:type 'String} + :total {:type :money} + :tip {:type :money} :expected_deposit {:type :expected_deposit}}}}) (def queries @@ -69,6 +73,7 @@ :date_range {:type :date_range} :total_lte {:type :money} :total_gte {:type :money} + :type_name {:type 'String} :processor {:type :processor} :start {:type 'Int} :per_page {:type 'Int} diff --git a/src/clj/auto_ap/jobs/square2.clj b/src/clj/auto_ap/jobs/square2.clj new file mode 100644 index 00000000..a6510360 --- /dev/null +++ b/src/clj/auto_ap/jobs/square2.clj @@ -0,0 +1,9 @@ +(ns auto-ap.jobs.square2 + (:gen-class) + (:require + [auto-ap.jobs.core :refer [execute]] + [auto-ap.square.core2 :as square2])) + +(defn -main [& _] + (execute "square2-loading" square2/upsert-all)) + diff --git a/src/clj/auto_ap/routes/ezcater.clj b/src/clj/auto_ap/routes/ezcater.clj index 45f1f741..caa1cd62 100644 --- a/src/clj/auto_ap/routes/ezcater.clj +++ b/src/clj/auto_ap/routes/ezcater.clj @@ -17,7 +17,6 @@ (POST "/event" request (log/info (str "POST EVENT " (body-string request) request)) (e/import-order (:json-params request)) - {:status 200 :headers {"Content-Type" "application/json"} :body "{}"})) diff --git a/src/clj/auto_ap/square/core.clj b/src/clj/auto_ap/square/core.clj index 5939f5e1..9590b9e5 100644 --- a/src/clj/auto_ap/square/core.clj +++ b/src/clj/auto_ap/square/core.clj @@ -449,7 +449,8 @@ :client/square-auth-token {:client/square-locations [:db/id :square-location/name :square-location/square-id :square-location/client-location]}]) ...] :in $ - :where [?c :client/square-auth-token]] + :where [?c :client/square-auth-token] + (not [?c :client/feature-flags "new-square"])] (d/db conn))) ([ & codes] (d/q '[:find [(pull ?c [:db/id @@ -458,6 +459,7 @@ {:client/square-locations [:db/id :square-location/name :square-location/square-id :square-location/client-location]}]) ...] :in $ [?code ...] :where [?c :client/square-auth-token] + (not [?c :client/feature-flags "new-square"]) [?c :client/code ?code]] (d/db conn) codes))) diff --git a/src/clj/auto_ap/square/core2.clj b/src/clj/auto_ap/square/core2.clj new file mode 100644 index 00000000..d4114664 --- /dev/null +++ b/src/clj/auto_ap/square/core2.clj @@ -0,0 +1,541 @@ +(ns auto-ap.square.core2 + (:require + [auto-ap.datomic :refer [conn remove-nils]] + [auto-ap.time :as atime] + [clj-http.client :as client] + [clj-time.coerce :as coerce] + [clj-time.core :as time] + [clj-time.format :as f] + [clj-time.periodic :as periodic] + [clojure.core.async :as async] + [clojure.data.json :as json] + [clojure.set :as set] + [clojure.string :as str] + [clojure.tools.logging :as log] + [cemerick.url :as url] + [datomic.api :as d] + [slingshot.slingshot :refer [try+]] + [unilog.context :as lc])) + +(defn client-base-headers [client] + {"Square-Version" "2021-08-18" + "Authorization" (str "Bearer " (:client/square-auth-token client)) + "Content-Type" "application/json"}) + +(defn retry-4 [ex try-count _] + (log/warn "Retrying after failure " ex) + (if (> try-count 4) false true)) + +(defn lookup-dates [] + (->> (periodic/periodic-seq (time/plus (time/now) (time/days -50)) + (time/now) + (time/days 5)) + (map (fn [d] + [(atime/unparse (time/plus d (time/days 1)) atime/iso-date) + + (atime/unparse (time/plus d (time/days 5)) atime/iso-date)])))) + +(defn client-locations [client] + (try + (->> (client/get "https://connect.squareup.com/v2/locations" + {:headers (client-base-headers client) + :as :json}) + :body + :locations) + (catch Exception e + (log/warn e) + []))) + +(defn fetch-catalog [client i] + (if i + (try + (log/trace "looking up catalog for" (str "https://connect.squareup.com/v2/catalog/object/" i)) + (->> (client/get (str "https://connect.squareup.com/v2/catalog/object/" i) + {:headers (client-base-headers client) + :query-params {"include_related_items" "true"} + :as :json}) + :body + :object) + (catch Exception e + (log/error e) + nil)) + (log/warn "Trying to look up non existant "))) + +(def fetch-catalog-fast (memoize fetch-catalog)) + +(defn item-id->category-name [client i] + (let [item (fetch-catalog-fast client i)] + (cond (:item_variation_data item) + (item-id->category-name client (:item_id (:item_variation_data item))) + + (:category_id (:item_data item)) + (:name (:category_data (fetch-catalog-fast client (:category_id (:item_data item))))) + + (:item_data item) + "Uncategorized" + + :else + (do + (log/warn "couldn't look up" i) + "Uncategorized")))) + +(defn pc [start end] + {"query" {"filter" {"date_time_filter" + { + "created_at" { + "start_at" (f/unparse (f/formatter "YYYY-MM-dd'T'HH:mm:ssZZ") start) + "end_at" (f/unparse (f/formatter "YYYY-MM-dd'T'HH:mm:ssZZ") end) + } + + + + }} + + "sort" { + "sort_field" "CREATED_AT" + "sort_order" "DESC" + }}}) + +#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} +(defn get-order + ([client location order-id] + (log/info "Searching for" (:square-location/client-location location)) + (let [result (->> (client/get (str "https://connect.squareup.com/v2/orders/" order-id) + {:headers (client-base-headers client) + :as :json}) + :body + )] + result))) + +(defn continue-search [client location start end cursor] + (log/info "Continuing search for" cursor) + (let [result (->> (client/post "https://connect.squareup.com/v2/orders/search" + {:headers (client-base-headers client) + :body (json/write-str (cond-> {"location_ids" [(:square-location/square-id location)] + "limit" 10000 + "cursor" cursor} + start (merge (pc start end)))) + :as :json}) + :body)] + (log/info "found " (count (:orders result))) + (if (not-empty (:cursor result)) + (concat (:orders result) (continue-search client location start end (:cursor result))) + (:orders result)))) + +(defn search + ([client location start end] + (log/info "Searching for" (:square-location/client-location location)) + (let [result (->> (client/post "https://connect.squareup.com/v2/orders/search" + {:headers (client-base-headers client) + :body (json/write-str (cond-> {"location_ids" [(:square-location/square-id location)] "limit" 10000} + start (merge (pc start end)))) + :as :json}) + :body)] + (log/info "found " (count (:orders result))) + (if (not-empty (:cursor result)) + (concat (:orders result) (continue-search client location start end (:cursor result))) + (:orders result))))) + + + +(defn amount->money [amt] + (* 0.01 (or (:amount amt) 0.0))) + + +;; to get totals: +(comment + (reduce + (fn [total i] + (+ total (+ (- (:sales-order/total i) + (:sales-order/tax i) + (:sales-order/tip i) + (:sales-order/service-charge i)) + (:sales-order/returns i) + + (:sales-order/discount i) + ))) + 0.0 + [])) + +(defn tender->charge [order client location t] + (remove-nils + #:charge + {:type-name (:type t) + :date (coerce/to-date (time/to-time-zone (coerce/to-date-time (:created_at order)) (time/time-zone-for-id "America/Los_Angeles"))) + :client (:db/id client) + :note (:note t) + :location (:square-location/client-location location) + :reference-link (str (url/url "https://squareup.com/receipt/preview" (:id t) )) + :external-id (when (:id t) + (str "square/charge/" (:id t))) + :processor (condp = (:type t) + "OTHER" + (condp = (some-> (:note t) str/lower-case) + "doordash" :ccp-processor/doordash + "dd" :ccp-processor/doordash + "ubereats" :ccp-processor/uber-eats + "ue" :ccp-processor/uber-eats + "grubhub" :ccp-processor/grubhub + "grub" :ccp-processor/grubhub + "gh" :ccp-processor/grubhub + (condp = (:name (:source order)) + "GRUBHUB" :ccp-processor/grubhub + "UBEREATS" :ccp-processor/uber-eats + "DOORDASH" :ccp-processor/doordash + "Koala" :ccp-processor/koala + "koala-production" :ccp-processor/koala + :ccp-processor/na)) + "CARD" + :ccp-processor/square + + "SQUARE_GIFT_CARD" + :ccp-processor/square + + "CASH" + :ccp-processor/na + + :ccp-processor/na) + :total (amount->money (:amount_money t)) + :tip (amount->money (:tip_money t))})) + +(defn order->sales-order [client location order] + (let [is-order-only-for-charge? + (= ["CUSTOM_AMOUNT"] + (mapv :item_type (:line_items order )))] + (if is-order-only-for-charge? + (->> (:tenders order) + (map #(tender->charge order client location %))) + [(remove-nils + #:sales-order + {:date (coerce/to-date (time/to-time-zone (coerce/to-date-time (:created_at order)) (time/time-zone-for-id "America/Los_Angeles"))) + :client (:db/id client) + :location (:square-location/client-location location) + :external-id (str "square/order/" (:client/code client) "-" (:square-location/client-location location) "-" (:id order)) + :source (or (:name (:source order)) + "Square") + :vendor :vendor/ccp-square + + :reference-link (str (url/url "https://squareup.com/dashboard/sales/transactions" (:id order) "by-unit" (:square-location/square-id location))) + :total (-> order :net_amounts :total_money amount->money) + :tax (-> order :net_amounts :tax_money amount->money) + :tip (-> order :net_amounts :tip_money amount->money) + :discount (-> order :net_amounts :discount_money amount->money) + :service-charge (-> order :net_amounts :service_charge_money amount->money) + :returns (+ (- (-> order :return_amounts :total_money amount->money) + (-> order :return_amounts :tax_money amount->money) + (-> order :return_amounts :tip_money amount->money) + (-> order :return_amounts :service_charge_money amount->money)) + (-> order :return_amounts :discount_money amount->money)) + :charges (->> (:tenders order) + (map #(tender->charge order client location %))) + :line-items (->> (:line_items order) + (map-indexed (fn [i li] + (remove-nils + #:order-line-item + {:external-id (str "square/order/" (:client/code client) "-" (:square-location/client-location location) "-" (:id order) "-" i) + :item-name (:name li) + :category (if (= "GIFT_CARD" (:item_type li)) + "Gift Card" + (item-id->category-name client (:catalog_object_id li))) + :total (amount->money (:total_money li)) + :tax (amount->money (:total_tax_money li)) + :discount (amount->money (:total_discount_money li))}))))})]))) + +(defn daily-results + ([client location] + (daily-results client location (time/plus (time/now) (time/days -45)) (time/now))) + ([client location start end] + (let [search-results (search client location start end)] + (->> search-results + (filter (fn [order] + ;; sometimes orders stay open in square. At least one payment + ;; is needed to import, in order to avoid importing orders in-progress. + (and + (or (> (count (:tenders order)) 0) + (seq (:returns order))) + (or (= #{} (set (map #(:status (:card_details %)) (:tenders order)))) + (not= #{} (set/difference + (set (map #(:status (:card_details %)) (:tenders order))) + #{"FAILED" "VOIDED"})))))) + (mapcat #(order->sales-order client location %)))))) + + +(defn retry + ([f] (retry f 0)) + ([f i] + (if (< i 5) + (try + (f) + (catch Exception e + (log/warn "error pulling http " e) + (retry f (inc i)))) + (log/warn "Too many failures")))) + +(defn get-payment [client p] + (:payment (:body (retry #(client/get (str "https://connect.squareup.com/v2/payments/" p) + {:headers (client-base-headers client) + :as :json + :retry-handler retry-4}))))) +(defn halt-if-error [x] + (if (instance? Throwable x) + (throw x) + x)) + +(defn get-settlement-sales-date [client settlement] + (let [concurrent 4 + output-chan (async/chan)] + (async/pipeline-blocking concurrent + output-chan + (map (fn [p] + (lc/with-context {:source "Square settlements loading "} + (log/trace "looking up payment " p " for settlement " (:id settlement)) + (or + (-> (get-payment client p) + :created_at + coerce/to-date) + (coerce/to-date (time/now)))))) + (async/to-chan! (->> settlement + :entries + (filter #(= "CHARGE" (:type %))) + (map :payment_id) + (filter identity) + (set) + (take 20) + )) + true + (fn [e] + (lc/with-context {:source "Square settlements loading "} + (log/warn "Error loading sales date details" e) + e))) + (->> (async/> (async/> lookup-dates + (mapcat (fn [[start-date end-date]] + (log/info "looking up settlements for " (:square-location/client-location location) " on dates " start-date " to " end-date) + (let [settlements (->> (retry #(client/get (str "https://connect.squareup.com/v1/" (:square-location/square-id location) "/settlements") + {:headers (client-base-headers client) + :query-params {"begin_time" start-date + "end_time" end-date} + :as :json + :retry-handler retry-4})) + :body + (map :id))] + settlements))) + set + seq + (get-settlement-details client location)))) + +(defn daily-settlements + ([client location] + (->> (for [settlement (settlements client location)] + #:expected-deposit {:external-id (str "square/settlement/" (:id settlement)) + :vendor :vendor/ccp-square + :status :expected-deposit-status/pending + :total (amount->money (:total_money settlement)) + :client (:db/id client) + :location (:square-location/client-location location) + :fee (- (reduce + 0.0 (map (fn [entry] + (if (= (:type entry) "REFUND") + (- (amount->money (:fee_money entry))) + (amount->money (:fee_money entry)))) + (:entries settlement)))) + :date (-> (:initiated_at settlement) + (coerce/to-date)) + :sales-date (or (:sales-date settlement) + (-> (:initiated_at settlement) + (coerce/to-date))) + :charges (->> (:entries settlement) + (filter :payment_id) + (map (fn [p] {:charge/external-id (str "square/charge/" (:payment_id p))})))}) + + (filter :expected-deposit/date)))) + +(defn refunds + ([client l] + (let [refunds (:refunds (:body (client/get (str "https://connect.squareup.com/v2/refunds?location_id=" (:square-location/square-id l)) + {:headers (client-base-headers client) + :as :json + :retry-handler retry-4})))] + (->> refunds + (filter (fn [r] (= "COMPLETED" (:status r)))) + (map (fn [r] + #:sales-refund {:external-id (str "square/refund/" (:id r)) + :vendor :vendor/ccp-square + :total (amount->money (:amount_money r)) + :fee (transduce + (comp (filter #(= "ADJUSTMENT" (:type %))) + (map :amount_money) + (map amount->money)) + + + 0.0 + (:processing_fee r)) + :client (:db/id client) + :location (:square-location/client-location l) + :date (coerce/to-date (:created_at r)) + :type (:source_type (get-payment client (:payment_id r)))})))))) + +(defn upsert + ([client ] + (doseq [square-location (:client/square-locations client) + :when (:square-location/client-location square-location)] + (upsert client square-location (time/plus (time/now) (time/days -45)) (time/now)))) + ([client location start end] + (lc/with-context {:source "Square loading"} + (doseq [x (partition-all 20 (daily-results client location start end))] + (log/info "Loading " (count x)) + @(d/transact conn x))))) + +(defn upsert-settlements + ([client] + (doseq [square-location (:client/square-locations client) + :when (:square-location/client-location square-location)] + (upsert-settlements client square-location))) + ([client location] + (lc/with-context {:source "Square settlements loading" + :client (:client/code client)} + (doseq [x (partition-all 20 (daily-settlements client location))] + (log/info "Loading expected deposit" (count x)) + @(d/transact conn x)) + (log/info "Done loading settlements")))) + +(defn upsert-refunds + ([client] + (doseq [square-location (:client/square-locations client) + :when (:square-location/client-location square-location)] + (upsert-refunds client square-location))) + ([client location] + (lc/with-context {:source "Loading Square Settlements" + :client (:client/code client) + :location (:square-location/client-location client)} + (doseq [x (partition-all 20 (refunds client location))] + (log/info "Loading refund" (count x)) + @(d/transact conn x)) + (log/info "Done loading refunds")))) + +(def square-read [:db/id + :client/code + :client/square-auth-token + {:client/square-locations [:db/id :square-location/name :square-location/square-id :square-location/client-location]}]) + +(defn get-square-clients + ([] + (d/q '[:find [(pull ?c [:db/id + :client/square-integration-status + :client/code + :client/square-auth-token + {:client/square-locations [:db/id :square-location/name :square-location/square-id :square-location/client-location]}]) ...] + :in $ + :where [?c :client/square-auth-token] + [?c :client/feature-flags "new-square"]] + (d/db conn))) + ([ & codes] + (d/q '[:find [(pull ?c [:db/id + :client/code + :client/square-auth-token + {:client/square-locations [:db/id :square-location/name :square-location/square-id :square-location/client-location]}]) ...] + :in $ [?code ...] + :where [?c :client/square-auth-token] + [?c :client/feature-flags "new-square"] + [?c :client/code ?code]] + (d/db conn) + codes))) + +(defn upsert-locations + ([] (doseq [client (get-square-clients)] + (upsert-locations client))) + ([client] + (let [square-id->id (into {} + (map + (fn [sl] + [(:square-location/square-id sl) + (:db/id sl)]) + (:client/square-locations client)))] + (->> (for [square-location (client-locations client)] + {:db/id (or (square-id->id (:id square-location)) (d/tempid :db.part/user)) + :client/_square-locations (:db/id client) + :square-location/name (:name square-location) + :square-location/square-id (:id square-location)}) + (d/transact conn) + deref)))) + +#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} +(defn reset [] + (->> + (d/query {:query {:find ['?e] + :in ['$] + :where ['(or [?e :sales-order/date] + [?e :expected-deposit/date])]} + :args [(d/db conn)]}) + (map first) + (map (fn [x] [:db/retractEntity x])))) + +(defn mark-integration-status [client integration-status] + @(d/transact conn + [{:db/id (:db/id client) + :client/square-integration-status (assoc integration-status + :db/id (or (-> client :client/square-integration-status :db/id) + #db/id [:db.part/user]))}])) + +(defn upsert-all [ & clients] + (doseq [client (apply get-square-clients clients) + :when (seq (filter :square-location/client-location (:client/square-locations client)))] + (lc/with-context {:client (:client/code client)} + (mark-integration-status client {:integration-status/last-attempt (coerce/to-date (time/now))}) + + (try+ + (upsert-locations client) + (upsert client) + (upsert-settlements client) + (upsert-refunds client) + (mark-integration-status client {:integration-status/state :integration-state/success + :integration-status/last-updated (coerce/to-date (time/now))}) + + (catch [:status 401] data + (mark-integration-status client {:integration-status/state :integration-state/unauthorized + :integration-status/message (-> data :body str)})) + + (catch [:status 503] data + (mark-integration-status client {:integration-status/state :integration-state/failed + :integration-status/message (-> data :body str)})) + + (catch Object _ + (log/warn &throw-context) + (mark-integration-status client {:integration-status/state :integration-state/failed + :integration-status/message (or (some-> (:wrapper &throw-context) (.getMessage )) + (some-> (:object &throw-context) str) + "Unknown error")})))))) + diff --git a/src/clj/user.clj b/src/clj/user.clj index f66623f3..e47305b0 100644 --- a/src/clj/user.clj +++ b/src/clj/user.clj @@ -498,8 +498,8 @@ (println "orders") (lc/with-context {:source "Historical loading data"} (doseq [d (per/periodic-seq (t/plus (t/today) (t/days (- days))) - (t/today) - (t/days 1))] + (t/today) + (t/days 1))] (println d) (square/upsert client square-location (c/to-date-time d) (c/to-date-time (t/plus d (t/days 1)))))) diff --git a/src/cljs/auto_ap/views/pages/pos/form.cljs b/src/cljs/auto_ap/views/pages/pos/form.cljs index b1b5f71c..15eaabdd 100644 --- a/src/cljs/auto_ap/views/pages/pos/form.cljs +++ b/src/cljs/auto_ap/views/pages/pos/form.cljs @@ -24,6 +24,14 @@ [form-builder/builder {:submit-event [::saving ] :id ::form} [form-builder/section {:title "Sales Order"} + [:div + "Order " (:id data) + (when (:reference-link data) + [:a {:href (:reference-link data) + :target "_new"} + [:span.icon + [:i.fa.fa-external-link + ]]])] (when-not @(re-frame/subscribe [::subs/client]) [form-builder/field-v2 {:field :client} "Client" @@ -62,7 +70,12 @@ [:ul (for [charge (:charges data)] ^{:key (:id charge)} - [:li (:type-name charge) ": " (:total charge)])]] + [:li [:span (:type-name charge) ": " (:total charge) + (when (:reference-link charge) + [:a {:href (:reference-link charge) :target "_new"} + [:span.icon + [:i.fa.fa-external-link + ]]])]])]] [form-builder/section {:title "Line Items"} [:ul diff --git a/src/cljs/auto_ap/views/pages/pos/sales_orders.cljs b/src/cljs/auto_ap/views/pages/pos/sales_orders.cljs index 7efe3182..1b4c6a0d 100644 --- a/src/cljs/auto_ap/views/pages/pos/sales_orders.cljs +++ b/src/cljs/auto_ap/views/pages/pos/sales_orders.cljs @@ -25,13 +25,14 @@ {:start (:start params 0) :sort (:sort params) :per-page (:per-page params) + :type-name (:type-name params) :total-gte (:amount-gte (:total-range params)) :total-lte (:amount-lte (:total-range params)) :date-range (:date-range params) :processor (some-> (:processor params) keyword) :client-id (:id @(re-frame/subscribe [::subs/client]))} - [[:sales-orders [:id :total :tax :tip :discount :service-charge :returns :date - [:charges [:type-name :total :processor :id [:expected-deposit [:id]] ]] + [[:sales-orders [:id :source :total :tax :tip :reference-link :discount :service-charge :returns :date + [:charges [:type-name :note :reference-link :total :processor :id [:expected-deposit [:id]] ]] [:line-items [:item-name :total :category]] [:client [:name :id]]]] :total diff --git a/src/cljs/auto_ap/views/pages/pos/side_bar.cljs b/src/cljs/auto_ap/views/pages/pos/side_bar.cljs index fd850128..981da9ac 100644 --- a/src/cljs/auto_ap/views/pages/pos/side_bar.cljs +++ b/src/cljs/auto_ap/views/pages/pos/side_bar.cljs @@ -39,9 +39,40 @@ (when (= :sales-orders ap) [:<> + [:p.menu-label "Payment Method"] + [:div + [:nav.panel + [:a.panel-block {:on-click (dispatch-event [::data-page/filter-changed data-page :type-name nil])} + [:span.panel-icon] + "All"] + [:a.panel-block {:on-click (dispatch-event [::data-page/filter-changed data-page :type-name "CASH"])} + [:span.panel-icon + [:span {:class "icon-accounting-bill" :style {:font-weight "400"}}]] + "Cash"] + [:a.panel-block {:on-click (dispatch-event [::data-page/filter-changed data-page :type-name "CARD"])} + [:span.panel-icon + [:span {:class "icon-credit-card-1" :style {:font-weight "400"}}]] + "Card"] + + [:a.panel-block {:on-click (dispatch-event [::data-page/filter-changed data-page :type-name "SQUARE_GIFT_CARD"])} + [:span.panel-icon + [:span {:class "icon-gift-box" :style {:font-weight "400"}}]] + "Gift Card"] + + + [:a.panel-block {:on-click (dispatch-event [::data-page/filter-changed data-page :type-name "OTHER"])} + [:span.panel-icon ] + "Other"]]] + [:p.menu-label "Processor"] [:div [:nav.panel + [:a.panel-block {:on-click (dispatch-event [::data-page/filter-changed data-page :processor nil])} + [:span.panel-icon] + "All"] + [:a.panel-block {:on-click (dispatch-event [::data-page/filter-changed data-page :processor "square"])} + [:span.panel-icon [:img.level-item {:src "/img/square.png"}]] + "Square"] [:a.panel-block {:on-click (dispatch-event [::data-page/filter-changed data-page :processor "doordash"])} [:span.panel-icon [:img.level-item {:src "/img/doordash.png"}]] "Doordash"] @@ -51,7 +82,17 @@ "Uber Eats"] [:a.panel-block {:on-click (dispatch-event [::data-page/filter-changed data-page :processor "grubhub"])} [:span.panel-icon [:img.level-item {:src "/img/grubhub.png"}]] - "Grubhub"]]]]) + "Grubhub"] + [:a.panel-block {:on-click (dispatch-event [::data-page/filter-changed data-page :processor "koala"])} + [:span.panel-icon [:img.level-item {:src "/img/koala.png"}]] + "Koala"] + [:a.panel-block {:on-click (dispatch-event [::data-page/filter-changed data-page :processor "ezcater"])} + [:span.panel-icon [:img.level-item {:src "/img/ezcater.png"}]] + "EZCater"] + [:a.panel-block {:on-click (dispatch-event [::data-page/filter-changed data-page :processor "na"])} + [:span.panel-icon #_[:img.level-item {:src "/img/grubhub.png"}]] + "No Processor"] + ]]]) (when-let [exact-match-id @(re-frame/subscribe [::data-page/filter data-page :exact-match-id])] [:div diff --git a/src/cljs/auto_ap/views/pages/pos/table.cljs b/src/cljs/auto_ap/views/pages/pos/table.cljs index 8536da47..597848ce 100644 --- a/src/cljs/auto_ap/views/pages/pos/table.cljs +++ b/src/cljs/auto_ap/views/pages/pos/table.cljs @@ -17,15 +17,36 @@ (defn row [{sales-order :sales-order selected-client :selected-client}] - (let [{:keys [client date total tax tip charges line-items id]} sales-order + (let [{:keys [client date total tax tip charges source line-items id]} sales-order expected-deposits (->> charges (filter :expected-deposit) (map :expected-deposit))] [grid/row {:class (:class sales-order) :id id} (when-not selected-client [grid/cell {} (:name client)]) [grid/cell {} (date->str date)] + [grid/cell {} source] [grid/cell {:class "has-text-right"} (nf total)] [grid/cell {:class "has-text-right"} (nf tax)] [grid/cell {:class "has-text-right"} (nf tip)] + [grid/cell {} + [:div.level-left + (for [charge charges] + + (with-meta + (condp = (:type-name charge) + "CASH" + [:span.icon.level-item {:style {:font-size "24px"}} [:span {:class "icon-accounting-bill" :style {:font-weight "400"}}]] + + "CARD" + [:span.icon.level-item {:style {:font-size "24px"}} [:span {:class "icon-credit-card-1" :style {:font-weight "400"}}]] + + "SQUARE_GIFT_CARD" + [:span.icon.level-item {:style {:font-size "24px"}} [:span {:class "icon-gift-box" :style {:font-weight "400"}}]] + + [:span.level-item [:span (:type-name charge) (when-let [note (:note charge)] + [:span + [:i.has-text-grey " (" note ")"]])] ]) + + {:key (:id charge)}))]] [grid/cell {} [:div.level [:div.level-left @@ -42,17 +63,15 @@ :uber-eats [:img.level-item {:src "/img/ubereats.png" :style {:width "24px" :height "24px"}}] - (condp = (:type-name charge) - "CASH" - [:span.icon.level-item {:style {:font-size "24px"}} [:span {:class "icon-accounting-bill" :style {:font-weight "400"}}]] + :square + [:img.level-item {:src "/img/square.png" :style {:width "24px" :height "24px"}}] - "CARD" - [:span.icon.level-item {:style {:font-size "24px"}} [:span {:class "icon-credit-card-1" :style {:font-weight "400"}}]] + :koala + [:img.level-item {:src "/img/koala.png" :style {:width "24px" :height "24px"}}] - "SQUARE_GIFT_CARD" - [:span.icon.level-item {:style {:font-size "24px"}} [:span {:class "icon-gift-box" :style {:font-weight "400"}}]] - - [:span.level-item "Other (" (:type-name charge) ")"])) + :ezcater + [:img.level-item {:src "/img/ezcater.png" :style {:width "24px" :height "24px"}}] + nil) {:key (:id charge)}))]]] @@ -105,10 +124,12 @@ (when-not selected-client [grid/sortable-header-cell {:sort-key "client" :sort-name "Client"} "Client"]) [grid/sortable-header-cell {:sort-key "date" :sort-name "Date" :style {:width "8em"}} "Date"] + [grid/sortable-header-cell {:sort-key "source" :sort-name "Source"} "Source"] [grid/sortable-header-cell {:sort-key "total" :sort-name "Total" :class "has-text-right" :style {:width "8em"}} "Total"] [grid/sortable-header-cell {:sort-key "tax" :sort-name "Tax" :class "has-text-right" :style {:width "7em"}} "Tax"] [grid/sortable-header-cell {:sort-key "tip" :sort-name "Tip" :class "has-text-right" :style {:width "7em"}} "Tip"] [grid/header-cell {} "Payment Methods"] + [grid/header-cell {} "Processor"] [grid/header-cell {} "Line Items"] [grid/header-cell {:style {:width "8em"}}]]] [grid/body diff --git a/test/clj/auto_ap/ezcater_test.clj b/test/clj/auto_ap/ezcater_test.clj index b6adc25d..f36859de 100644 --- a/test/clj/auto_ap/ezcater_test.clj +++ b/test/clj/auto_ap/ezcater_test.clj @@ -86,48 +86,23 @@ "2022-06-01T07:00:00Z") (sut/order->sales-order) (:sales-order/date ))))) - (t/testing "It should categorize every item as 'External Catering'" - (t/is (= 2 + (t/testing "It should simulate a single line item for everything" + (t/is (= 1 (-> known-order sut/order->sales-order :sales-order/line-items count))) - (t/is (= #{"External Catering"} + (t/is (= #{"EZCater Catering"} (->> known-order sut/order->sales-order :sales-order/line-items (map :order-line-item/category) set)))) - - (t/testing "It should generate an id for every line item" - (t/is (= ["ezcater/order/ABC-DT-9ab05fee-a9c5-483b-a7f2-14debde4b7a8-0" - "ezcater/order/ABC-DT-9ab05fee-a9c5-483b-a7f2-14debde4b7a8-1"] - (->> known-order - sut/order->sales-order - :sales-order/line-items - (map :order-line-item/external-id))))) - (t/testing "It should generate an external-id" (t/is (= "ezcater/order/ABC-DT-9ab05fee-a9c5-483b-a7f2-14debde4b7a8" (:sales-order/external-id (sut/order->sales-order known-order))))) - - (t/testing "Should include package name" - (t/is (= #{"Spartan Package"} - (->> known-order - sut/order->sales-order - :sales-order/line-items - (map :order-line-item/item-name) - set)))) - - (t/testing "Should use the total amount" - (t/is (= [34.29 206.75] - (->> (-> known-order - (assoc-in [:catererCart :orderItems 0 :totalInSubunits :subunits] 3429) - (assoc-in [:catererCart :orderItems 1 :totalInSubunits :subunits] 20675)) - sut/order->sales-order - :sales-order/line-items - (map :order-line-item/total))))) + (t/testing "Should capture amounts" (t/is (= 35.09 (-> known-order @@ -147,7 +122,7 @@ (-> known-order (assoc :orderSourceType "EZCATER") (assoc-in [:totals :subTotal :subunits] 10000) - (assoc-in [:catererCart :feesAndDiscounts 0 :subunits] 10000) + (assoc-in [:catererCart :feesAndDiscounts 0 :cost :subunits] 10000) sut/commision))))) (t/testing "Should calculate 15% commision on marketplace orders" (t/is (dollars= 15.0 @@ -160,7 +135,7 @@ (-> known-order (assoc :orderSourceType "MARKETPLACE") (assoc-in [:totals :subTotal :subunits] 10000) - (assoc-in [:catererCart :feesAndDiscounts 0 :subunits] 10000) + (assoc-in [:catererCart :feesAndDiscounts 0 :cost :subunits] 10000) sut/commision))))) (t/testing "Should calculate 2.75% ccp fee" (t/is (dollars= 8.25 @@ -168,10 +143,10 @@ (assoc :orderSourceType "MARKETPLACE") (assoc-in [:totals :subTotal :subunits] 10000) (assoc-in [:totals :salesTax :subunits] 10000) - (assoc-in [:catererCart :feesAndDiscounts 0 :subunits] 10000) + (assoc-in [:catererCart :feesAndDiscounts 0 :cost :subunits] 10000) sut/ccp-fee)))) (t/testing "Should use ezcater total paid to the customer" - (t/is (dollars= 420.65 + (t/is (dollars= 454.09 (-> known-order sut/order->sales-order :sales-order/total)))) @@ -179,6 +154,14 @@ (t/is (dollars= -41.8975 (-> known-order sut/order->sales-order - :sales-order/discount))))) + :sales-order/discount)))) + + (t/testing "Should create a charge for the order" + (t/is (dollars= 454.09 + (-> known-order + sut/order->sales-order + :sales-order/charges + first + :charge/total)))))