國(guó)家稅務(wù)總局電子發(fā)票查詢下載
全電發(fā)票受票試點(diǎn)全面鋪開,全國(guó)每個(gè)省、自治區(qū)、直轄市、計(jì)劃單列市都能收取全電發(fā)票了!作為我們會(huì)計(jì)人員犯愁了,收到全電發(fā)票,如何勾選認(rèn)證呢?如果作為可以開具全電發(fā)票的城市如何開具全電發(fā)票呢?發(fā)票開錯(cuò)了,
2024.11.24用一系列文章介紹如何用python寫一個(gè)發(fā)票管理小工具。
本文介紹如何用Uvicorn+FastAPI搭建后臺(tái)接口,如何使用loguru打印日志,以及自動(dòng)打開瀏覽器顯示前端頁(yè)面。
代碼結(jié)構(gòu)
大家一般運(yùn)行Uvicorn都是uvicorn.run()直接運(yùn)行,這樣uvicorn啟動(dòng)后會(huì)阻塞程序,后續(xù)無(wú)法執(zhí)行代碼。
所以這里我們使用Python多進(jìn)程,一個(gè)主進(jìn)程用于打開瀏覽器以及后續(xù)其他功能,一個(gè)獨(dú)立的子進(jìn)程運(yùn)行Uvicorn。
創(chuàng)建一個(gè)配置文件config.yml,編寫config.py用于讀取配置文件。使用ruamel.yaml模塊讀取yaml文件。
#config.pyfrom ruamel.yaml import YAMLclass AppConfig():def __init__(self):with open("config/config.yml","r",encoding="utf-8") as configFile:yaml = YAML()conf = yaml.load(configFile)self.config = confself.loguru = self.config['loguru']self.uvicorn = self.config['uvicorn']appConfig = AppConfig()
# config.yml# 日志配置loguru:format: '{time:YYYY-MM-DDTHH:mm:ss.SSSZ} {level} tid[{extra[tid]}] {message}'level: DEBUGfile: logs/application.logrotation: 00:00retention: 30 days# uvicorn設(shè)置uvicorn:host: 127.0.0.1port: 8088# 等待web服務(wù)啟動(dòng)時(shí)間,服務(wù)啟動(dòng)后自動(dòng)打開瀏覽器waitWebServerTime: 10
使用multiprocessing.Process創(chuàng)建子進(jìn)程,注意設(shè)置daemon=True,這樣主進(jìn)程關(guān)閉時(shí)會(huì)自動(dòng)關(guān)掉子進(jìn)程。
import uvicornfrom multiprocessing import Processfrom config import appConfigdef runUvicorn():uvicorn.run("webapi:app", host=appConfig.uvicorn["host"], port=appConfig.uvicorn["port"], reload=False, log_config='config/uvicornLog.json', access_log=True, workers=1)# 啟動(dòng)web服務(wù)webapiProcess = Process(target=runUvicorn, name="webapi_process", daemon=True)webapiProcess.start()try:while True:time.sleep(1)except KeyboardInterrupt: logger.info("user interrupt, close application")sys.exit(0)
為了方便日志追蹤,一般我們都會(huì)在日志中打印一個(gè)transaction_id,用戶一個(gè)請(qǐng)求過(guò)程中的所有日志都有這樣一個(gè)唯一的transaction_id。
我們使用loguru模塊來(lái)打印日志,loguru配置和使用非常簡(jiǎn)單,而且我們的程序使用了多進(jìn)程,loguru是支持多進(jìn)程的。
第一節(jié)的配置文件中已經(jīng)有l(wèi)oguru需要的配置項(xiàng)了,下面介紹如何使用loguru,以及fastapi如何生成transaction_id,并且將tid添加到日志中。
創(chuàng)建log.py
from loguru import loggerimport sysfrom config import appConfiglogger.remove()logger.configure(extra={"tid":""})logger.add(sys.stderr,enqueue=True, format=appConfig.loguru["format"], level=appConfig.loguru["level"])logger.add(appConfig.loguru["file"], enqueue=True, format=appConfig.loguru["format"], level=appConfig.loguru["level"], rotation=appConfig.loguru["rotation"], retention=appConfig.loguru["retention"])
需要關(guān)注的是logger.configure(extra={"tid":""})這一行,將tid添加到日志record的extra中,extra是loguru提供給用戶添加自定義日志字段的屬性。雖然也有辦法直接修改record,但是建議添加到extra中。
在webapi.py中添加middleware,在收到請(qǐng)求時(shí)生成tid。然后通過(guò)loguru的pach將tid添加到日志record中。
#webapi.pyfrom random import randintfrom typing import Callable, Optionalfrom fastapi import FastAPI,Requestfrom loguru import loggerfrom datetime import datetimefrom contextvars import ContextVar# 用于日志記錄當(dāng)前請(qǐng)求IDtransaction_id: ContextVar[Optional[str]] = ContextVar('transaction_id', default='')def transaction_id_filter(record):record['extra']['tid'] = transaction_id.get()return record# 此處向日志中設(shè)置tidlogger.configure(patcher=transaction_id_filter)app = FastAPI(title='My Invoice Folder', docs_url=None, redoc_url=None)# 生成請(qǐng)求ID@app.middleware("http")async def create_transaction_id(request: Request, call_next: Callable):# tid格式為時(shí)間戳+4位隨機(jī)數(shù)transaction_id.set(f"{datetime.now().strftime('%Y%m%d%H%M%S%f')[:-3]}{randint(1000, 9999)}")response = await call_next(request)return response
我們之前已經(jīng)寫了一個(gè)發(fā)票夾html頁(yè)面,這里直接將fastapi首頁(yè)跳轉(zhuǎn)到這個(gè)頁(yè)面就可以了。
將頁(yè)面資源都放到項(xiàng)目ui文件夾中,加載ui文件夾。
app.mount("/ui", StaticFiles(directory="ui"), name="ui")
設(shè)置首頁(yè)跳轉(zhuǎn)
# 首頁(yè)跳轉(zhuǎn)到我的發(fā)票夾頁(yè)面@app.get("/", include_in_schema=False)def read_root():logger.info("access index page")return RedirectResponse("/ui/index.html")
首先我們?cè)趩?dòng)Uvicorn進(jìn)程后,使用request請(qǐng)求首頁(yè),如果請(qǐng)求成功,則打開系統(tǒng)默認(rèn)瀏覽器并打開發(fā)票夾首頁(yè)。代碼如下:
# 啟動(dòng)系統(tǒng)默認(rèn)瀏覽器打開界面waitWebServerTime = appConfig.config["waitWebServerTime"]openBrowserSuccess = Falsewhile waitWebServerTime > 0:time.sleep(1)try: # 判斷web服務(wù)是否已啟動(dòng)indexUrl = f'http://{appConfig.uvicorn["host"]}:{appConfig.uvicorn["port"]}/'response = requests.get(indexUrl, timeout=1)if response.status_code == 200:# 打開主頁(yè)openBrowserSuccess = openBrowser(indexUrl)breakexcept Exception as e:passwaitWebServerTime -= 1
現(xiàn)在我們可以直接運(yùn)行main.py,就可以運(yùn)行后端api,并且自動(dòng)打開瀏覽器看到頁(yè)面了~后續(xù)邏輯抽空繼續(xù)寫。
控制臺(tái)日志
自動(dòng)打開瀏覽器,訪問(wèn)本地頁(yè)面
代碼比較多,已經(jīng)上傳到github(https://github.com/xy12358/my-invoice-folder.git)和gitee(https://gitee.com/xinying/my-invoice-folder)。
全電發(fā)票受票試點(diǎn)全面鋪開,全國(guó)每個(gè)省、自治區(qū)、直轄市、計(jì)劃單列市都能收取全電發(fā)票了!作為我們會(huì)計(jì)人員犯愁了,收到全電發(fā)票,如何勾選認(rèn)證呢?如果作為可以開具全電發(fā)票的城市如何開具全電發(fā)票呢?發(fā)票開錯(cuò)了,
2024.11.24數(shù)字化電子發(fā)票全流 程系統(tǒng)操作問(wèn)題解答國(guó)家稅務(wù)總局上海市稅務(wù)局第四稅務(wù)分局 2022 年 11 月編寫說(shuō)明按照稅務(wù)總局發(fā)票電子化改革統(tǒng)一部署, 以 12 月底實(shí) 現(xiàn)數(shù)字化電子發(fā)票開票全覆蓋為目標(biāo),本市
2024.11.24稅務(wù)征期即“納稅申報(bào)日期”。一般納稅人企業(yè)需要在每月初進(jìn)行抄報(bào)稅,申報(bào)上一月度開發(fā)票信息及增值稅申報(bào)情況,并繳納稅款。 征期一般是每月1-15日,遇到連續(xù)超過(guò)3天的節(jié)假日會(huì)按放假的天數(shù)順延。征期最后一
2024.11.24關(guān)鍵詞:發(fā)票識(shí)別 私有云發(fā)票識(shí)別 發(fā)票識(shí)別API接口 webservice發(fā)票識(shí)別平臺(tái)發(fā)票,一個(gè)再也熟悉不過(guò)的財(cái)務(wù)往來(lái)憑證,錄入發(fā)票,一項(xiàng)讓多少財(cái)會(huì)人員頭疼的工作。過(guò)去錄入一張發(fā)票需要一個(gè)財(cái)會(huì)人員5分
2024.11.24發(fā)票,相信大家都不陌生,對(duì)于公司來(lái)講,發(fā)票主要是公司做賬的依據(jù),同時(shí)也是繳稅的費(fèi)用憑證;而對(duì)于員工來(lái)講,發(fā)票主要是用來(lái)報(bào)銷的。對(duì)于收到的發(fā)票,會(huì)計(jì)需要統(tǒng)計(jì)、核查、錄入到企業(yè)的財(cái)務(wù)系統(tǒng),而這幾個(gè)環(huán)節(jié)往往
2024.11.24