Great, Ill just create a cron to move them!
When running the install, it all seems well.
erpnext@erp-test:/srv/bench/erpnext$ bench version
erpnext 12.x.x-develop
frappe 12.x.x-develop
pdf_on_submit 0.0.1
However, even after the restart, the search for PDF on Submit Settings does not provide any result. Do I oversee anything? Do I need to enable the app somewhere?
Thanks!
Edit: additionally, in About, I see the following:
Frappe Framework
Open Source Applications for the Web
Website: https://frappe.io
Source: Frappe Ā· GitHub
Installed Apps
ERPNext: v12.x.x-develop () (develop)
Frappe Framework: v12.x.x-develop () (develop)
Edit: Solved. Im a retard. Didnt enable properly
Youāre the best, thanks for sharing your plugin.
I solved it as follow with a bash script:
#!/usr/bin/env bash
shopt -s nullglob extglob
dest=/mnt/invoices/
src=/srv/bench/erpnext/sites/YOURSITEHERE/private/files/
now=$(date)
echo "Started script @ $now"
for item in src*; do
if [[ $item == *YOUR DOCUMENT RANGE BETWEEN THE ASTERIX* ]]
#SINV, SORD, etc, whatever your name your documents
then
mv "$item" "$dest${item##*/}" || exit
echo "${item##*/} moved"
fi
done
Additionally I created a script which through WEBDAVS has the same location mounted, which allows it to be printed, over an encrypted WEBDAV connection (nextcloud). This way, in my case, when my brother issues an invoice, it is printed at my moms house for her administration.
@rmeyer nice App.
Just downloaded and testing⦠cool.
Maybe add this to your app if you think is a + point⦠an option where user could select the name of the PDF (name, other custom field, etc)
@rmeyer
Very pleased eith your app. Do you think it would be possible to include delivery notes in future as well?
edit: nevermind⦠helps if you look into the right environment when testing⦠running in test environment checking if it exists in production, doesnāt really helpā¦
We had some experience with stolen goods during transport. Iāve writter below Python script (please dont be harsh, its my first Python script), to use a Telegram bot to add images to the delivery note folder on a Nextcloud instance. It consists of two scripts; One moving the PDFās to a folder by Customer/Delivery and then the second allowing to add images to this folder through Telegram.
Could probably be done more neat, but works for now, but any Security concerns I would be happy to hear.
import mysql.connector
import requests
import os
import shutil
def left(s, amount):
return s[:amount]
folder = "[NEXT CLOUD MOUNT FOLDER]/Deliveries/"
rejected_folder = "[NEXT CLOUD MOUNT FOLDER]/Deliveries/Rejected/"
db = mysql.connector.connect(host="[HOST]", user="[USER]", passwd="[PW]", db="[ERP NEXT DB]")
mycursor = db.cursor()
if not os.path.exists(rejected_folder):
os.makedirs(rejected_folder)
for file in os.listdir(folder):
if not file.lower().endswith(".pdf"):
if os.path.isdir(folder + file):
print(file + " is a directory")
else:
print(file + " is not a pdf")
shutil.move(folder + file, rejected_folder + file)
continue
doc = left(file,17)
try:
mycursor.execute("""SELECT `customer` from `tabDelivery Note` where name = '%s'""" % doc)
customer = mycursor.fetchone()
customer = customer[0].replace(" ","_")
if not os.path.exists(folder + customer + "/" + doc):
os.makedirs(folder + customer + "/" + doc)
print (file + " -> " + doc + " -> " + customer)
shutil.move(folder + file, folder + customer + "/" + doc + "/" + file)
except:
print("Error")
The Telegram Bot script:
#!/usr/bin/env python3
# pylint: disable=W0613, C0116
# type: ignore[union-attr]
import logging
import datetime
from random import randint
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update
from telegram.ext import (
Updater,
CommandHandler,
MessageHandler,
Filters,
ConversationHandler,
CallbackContext,
)
import mysql.connector
import requests
from fuzzywuzzy import process
import os
import shutil
def left(s, amount):
return s[:amount]
url = "[HERE YOUR NEXTCLOUD URL/FOLDER/ WHERE FILES ARE STORED"
folder = "[MOUNT LOCATION OF MY NEXTCLOUD INSTANCE/Deliveries/"
rejected_folder = "[MOUNT LOCATION OF MY NEXTCLOUD INSTANCE/Deliveries/Rejected/"
# Enable logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
CUSTOMER, MATDOC, PHOTO, PHOTO_CHOICE = range(4)
#Query the database and return the potential customers based on supplied variable
def start(update: Update, context: CallbackContext) -> int:
db = mysql.connector.connect(host="[HOST]", user="[USER]", passwd="[PASS]", db="[DB ERPNEXT]")
mycursor = db.cursor()
cust = update.message.text.split()
klant = cust[1]
mycursor.execute("""SELECT `name` from `tabCustomer`""")
myresult = list(mycursor.fetchall())
mycursor.close()
db.close()
#put three best matches in a list
fuzz_cust = process.extract(klant,myresult, limit=3)
logger.info(fuzz_cust)
#for score, matchrow in process.extract(row, myresult):
# if score >= 60:
# logger.info('%d%% partial match: "%s" with "%s" ' % (score, row))
Digi_Results = [[fuzz_cust[0][0][0]],[fuzz_cust[1][0][0]],[fuzz_cust[2][0][0]], ["/cancel"]]
logger.info(Digi_Results)
update.message.reply_text(
'Select the customer:',
reply_markup=ReplyKeyboardMarkup(Digi_Results, one_time_keyboard=True),
)
return CUSTOMER
def customer(update: Update, context: CallbackContext) -> int:
matdocs = [["Yes"], ["No"], ["/cancel"]]
user = update.message.from_user
logger.info("Selected customer %s", update.message.text)
global cust
cust = update.message.text
db = mysql.connector.connect(host="[HOST]", user="[USER]", passwd="[PASS]", db="[DB ERPNEXT]")
mycursor = db.cursor()
mycursor.execute("""SELECT `name` from `tabDelivery Note` WHERE `customer` = '%s' AND `status` <> 'Cancelled' ORDER BY `creation` DESC LIMIT 3""" % cust)
myresult = list(mycursor.fetchall())
myresult.append(["/cancel"])
update.message.reply_text(
update.message.text + " selected.\n\n"
"Select Delivery Note or Cancel",
reply_markup=ReplyKeyboardMarkup(myresult, one_time_keyboard=True),
)
mycursor.close()
db.close()
return MATDOC
def matdoc(update: Update, context: CallbackContext) -> int:
global matdc
matdc = update.message.text
global store
store = ""
update.message.reply_text(
update.message.text + " selected.\n\n"
"Send in photos. One at a time."
)
return PHOTO
def photo(update: Update, context: CallbackContext) -> int:
user = update.message.from_user
photo_file = update.message.photo[-1].get_file()
#photo_file.download('img/' + str(datetime.date.today()) + '_' + str(randint(0, 10000)) + '.jpg')
doc = left(matdc,17)
customer = cust.replace(" ","_")
photo_ref = str(datetime.date.today()) + '_' + str(randint(0, 10000)) + '.jpg'
if not os.path.exists(folder + customer + "/" + doc):
os.makedirs(folder + customer + "/" + doc)
global loc
loc = (url + customer + "/" + doc)
photo_file.download(folder + customer + "/" + doc + "/" + photo_ref)
re = [["Yes"], ["No"], ["/cancel"]]
update.message.reply_text(
"Your image is stored with customer: " + cust + ", delivery: "+left(matdc,17)+ ", want to add another?",
reply_markup=ReplyKeyboardMarkup(re, one_time_keyboard=True),
)
return PHOTO_CHOICE
def photo_choice(update: Update, context: CallbackContext) -> int:
user = update.message.from_user
choice = update.message.text
if choice == "Yes":
update.message.reply_text(
'Ok. Send a new picture.'
)
return PHOTO
else:
user = update.message.from_user
logger.info("User is done.")
update.message.reply_text("Your images are stored on " + loc + ". You can access them on Nextcloud.\n\nYou can start a new session by sending /photo + {customer}. This session will be ended. ")
return ConversationHandler.END
def cancel(update: Update, context: CallbackContext) -> int:
user = update.message.from_user
logger.info("User %s canceled the conversation.", user.first_name)
update.message.reply_text(
'Transaction closed. Use /photo + {customer} to start a new session.', reply_markup=ReplyKeyboardRemove()
)
return ConversationHandler.END
def main() -> None:
# Create the Updater and pass it your bot's token.
updater = Updater("YOUR BOTS TOKEN")
# Get the dispatcher to register handlers
dispatcher = updater.dispatcher
# Conversation handler
conv_handler = ConversationHandler(
entry_points=[CommandHandler('photo', start)],
states={
CUSTOMER: [MessageHandler(Filters.text & ~Filters.command, customer)],
MATDOC: [MessageHandler(Filters.text & ~Filters.command, matdoc)],
PHOTO: [MessageHandler(Filters.photo, photo)],
PHOTO_CHOICE: [MessageHandler(Filters.text & ~Filters.command, photo_choice)]
#SUMMARY: [MessageHandler(Filters.text & ~Filters.command, summary)]
},
fallbacks=[CommandHandler('cancel', cancel)],
)
dispatcher.add_handler(conv_handler)
# Start the Bot
updater.start_polling()
# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
Edit:
So with /photo {customername} the bot will send three best matching customers to choose from. Based on this choice it shows latest three Material documents, you can select. From there you can submit photoās which will be stored in the same folder as the material document.
When a customer has question about the state it received the goods, I have an image of how it looked when it was shipped.
Interesting feature.
This way the company can āprintā the pdf on supplier or buyerās office.
Hi I use ERPNext v13 hosted on frappe cloud. I tried uninstalling and reinstalling PDF on Submit to go from v13 to v14 for this app but Iām only showed v13 on Frappe āAdd Appā.
Is it because Iām using ERPNext v13 ? Should I upgrade ?
Thanks
Yes, major versions are intended to match.
You need to use PDF on Submit v13 on ERPNext v13, just like you need Frappe v13 to run ERPNext v13.
Ok, thanks I get it, that makes sense !